FSF GCC merge 02/23/03
[official-gcc.git] / gcc / ada / gnatprep.adb
blob2d88e5426666851f5ed70010ac09b9209f2e1cbe
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT COMPILER COMPONENTS --
4 -- --
5 -- G N A T P R E P --
6 -- --
7 -- B o d y --
8 -- --
9 -- --
10 -- Copyright (C) 1996-2002, Free Software Foundation, Inc. --
11 -- --
12 -- GNAT is free software; you can redistribute it and/or modify it under --
13 -- terms of the GNU General Public License as published by the Free Soft- --
14 -- ware Foundation; either version 2, or (at your option) any later ver- --
15 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
16 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
17 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
18 -- for more details. You should have received a copy of the GNU General --
19 -- Public License distributed with GNAT; see file COPYING. If not, write --
20 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
21 -- MA 02111-1307, USA. --
22 -- --
23 -- GNAT was originally developed by the GNAT team at New York University. --
24 -- Extensive contributions were provided by Ada Core Technologies Inc. --
25 -- --
26 ------------------------------------------------------------------------------
28 with Ada.Characters.Handling; use Ada.Characters.Handling;
29 with Ada.Strings.Fixed;
30 with Ada.Command_Line; use Ada.Command_Line;
31 with Ada.Text_IO; use Ada.Text_IO;
33 with GNAT.Heap_Sort_G;
34 with GNAT.Command_Line;
36 with Gnatvsn;
38 procedure GNATprep is
40 type Strptr is access String;
42 Usage_Error : exception;
43 -- Raised if a usage error is detected, causes termination of processing
44 -- with an appropriate error message and error exit status set.
46 Fatal_Error : exception;
47 -- Exception raised if fatal error detected
49 Expression_Error : exception;
50 -- Exception raised when an invalid boolean expression is found
51 -- on a preprocessor line
53 ------------------------
54 -- Argument Line Data --
55 ------------------------
57 Outfile_Name : Strptr;
58 Deffile_Name : Strptr;
59 -- Names of files
61 type Input;
62 type Input_Ptr is access Input;
63 type Input is record
64 File : File_Type;
65 Next : Input_Ptr;
66 Prev : Input_Ptr;
67 Name : Strptr;
68 Line_Num : Natural := 0;
69 end record;
70 -- Data for the current input file (main input file or included file
71 -- or definition file).
73 Infile : Input_Ptr := new Input;
74 Outfile : File_Type;
75 Deffile : File_Type;
77 Opt_Comment_Deleted_Lines : Boolean := False; -- Set if -c switch set
78 Blank_Deleted_Lines : Boolean := False; -- Set if -b switch set
79 List_Symbols : Boolean := False; -- Set if -s switch set
80 Source_Ref_Pragma : Boolean := False; -- Set if -r switch set
81 Undefined_Is_False : Boolean := False; -- Set if -u switch set
82 -- Record command line options
84 ---------------------------
85 -- Definitions File Data --
86 ---------------------------
88 Num_Syms : Natural := 0;
89 -- Number of symbols defined in definitions file
91 Symbols : array (0 .. 10_000) of Strptr;
92 Values : array (0 .. 10_000) of Strptr;
93 -- Symbol names and values. Note that the zero'th element is used only
94 -- during the call to Sort (to hold a temporary value, as required by
95 -- the GNAT.Heap_Sort_G interface).
97 ---------------------
98 -- Input File Data --
99 ---------------------
101 Current_File_Name : Strptr;
102 -- Holds name of file being read (definitions file or input file)
104 Line_Buffer : String (1 .. 20_000);
105 -- Hold one line
107 Line_Length : Natural;
108 -- Length of line in Line_Buffer
110 Ptr : Natural;
111 -- Input scan pointer for line in Line_Buffer
113 type Keyword is (K_Not, K_Then, K_If, K_Else, K_End, K_Elsif,
114 K_And, K_Or, K_Open_Paren, K_Close_Paren,
115 K_Defined, K_Andthen, K_Orelse, K_Equal, K_Include,
116 K_None);
117 -- Keywords that are recognized on preprocessor lines. K_None indicates
118 -- that no keyword was present.
120 K : Keyword;
121 -- Scanned keyword
123 Start_Sym, End_Sym : Natural;
124 -- First and last positions of scanned symbol
126 Num_Errors : Natural := 0;
127 -- Number of errors detected
129 -----------------------
130 -- Preprocessor Data --
131 -----------------------
133 -- The following record represents the state of an #if structure:
135 type PP_Rec is record
136 If_Line : Positive;
137 -- Line number for #if line
139 If_Name : Strptr;
140 -- File name of #if line
142 Else_Line : Natural;
143 -- Line number for #else line, zero = no else seen yet
145 Deleting : Boolean;
146 -- True if lines currently being deleted
148 Match_Seen : Boolean;
149 -- True if either the #if condition or one of the previously seen
150 -- #elsif lines was true, meaning that any future #elsif sections
151 -- or the #else section, is to be deleted.
153 end record;
155 PP_Depth : Natural;
156 -- Preprocessor #if nesting level. A value of zero means that we are
157 -- outside any #if structure.
159 PP : array (0 .. 100) of PP_Rec;
160 -- Stack of records showing state of #if structures. PP (1) is the
161 -- outer level entry, and PP (PP_Depth) is the active entry. PP (0)
162 -- contains a dummy entry whose Deleting flag is always set to False.
164 -----------------
165 -- Subprograms --
166 -----------------
168 function At_End_Of_Line return Boolean;
169 -- First advances Ptr using Skip_Spaces. Then returns True if Ptr is
170 -- either at the end of the line, or at a -- comment sequence.
172 procedure Error (Msg : String);
173 -- Post error message with given text. The line number is taken from
174 -- Infile.Line_Num, and the column number from Ptr.
176 function Eval_Condition
177 (Parenthesis : Natural := 0;
178 Do_Eval : Boolean := True)
179 return Boolean;
180 -- Eval the condition found in the current Line. The condition can
181 -- include any of the 'and', 'or', 'not', and parenthesis subexpressions.
182 -- If Line is an invalid expression, then Expression_Error is raised,
183 -- after an error message has been printed. Line can include 'then'
184 -- followed by a comment, which is automatically ignored. If Do_Eval
185 -- is False, then the expression is not evaluated at all, and symbols
186 -- are just skipped.
188 function Eval_Symbol (Do_Eval : Boolean) return Boolean;
189 -- Read and evaluate the next symbol or expression (A, A'Defined, A=...)
190 -- If it is followed by 'Defined or an equality test, read as many symbols
191 -- as needed. Do_Eval has the same meaning as in Eval_Condition
193 procedure Help_Page;
194 -- Print a help page to summarize the usage of gnatprep
196 function Image (N : Natural) return String;
197 -- Returns Natural'Image (N) without the initial space
199 function Is_Preprocessor_Line return Boolean;
200 -- Tests if current line is a preprocessor line, i.e. that its first
201 -- non-blank character is a # character. If so, then a result of True
202 -- is returned, and Ptr is set to point to the character following the
203 -- # character. If not, False is returned and Ptr is undefined.
205 procedure No_Junk;
206 -- Make sure no junk is present on a preprocessor line. Ptr points past
207 -- the scanned preprocessor syntax.
209 function OK_Identifier (S : String) return Boolean;
210 -- Tests if given referenced string is valid Ada identifier
212 function Matching_Strings (S1, S2 : String) return Boolean;
213 -- Check if S1 and S2 are the same string (this is a case independent
214 -- comparison, lower and upper case letters are considered to match).
215 -- Duplicate quotes in S2 are considered as a single quote ("" => ")
217 procedure Parse_Def_File;
218 -- Parse the deffile given by the user
220 function Scan_Keyword return Keyword;
221 -- Advances Ptr to end of line or next non-blank using Skip_Spaces. Then
222 -- attempts to scan out a recognized keyword. if a recognized keyword is
223 -- found, sets Ptr past it, and returns the code for the keyword, if not,
224 -- then Ptr is left unchanged pointing to a non-blank character or to the
225 -- end of the line.
227 function Symbol_Scanned return Boolean;
228 -- On entry, Start_Sym is set to the first character of an identifier
229 -- symbol to be scanned out. On return, End_Sym is set to the last
230 -- character of the identifier, and the result indicates if the scanned
231 -- symbol is a valid identifier (True = valid). Ptr is not changed.
233 procedure Skip_Spaces;
234 -- Skips Ptr past tabs and spaces to next non-blank, or one character
235 -- past the end of line.
237 function Variable_Index (Name : String) return Natural;
238 -- Returns the index of the variable in the table. If the variable is not
239 -- found, returns Natural'Last
241 --------------------
242 -- At_End_Of_Line --
243 --------------------
245 function At_End_Of_Line return Boolean is
246 begin
247 Skip_Spaces;
249 return Ptr > Line_Length
250 or else
251 (Ptr < Line_Length and then Line_Buffer (Ptr .. Ptr + 1) = "--");
252 end At_End_Of_Line;
254 -----------
255 -- Error --
256 -----------
258 procedure Error (Msg : String) is
259 L : constant String := Natural'Image (Infile.Line_Num);
260 C : constant String := Natural'Image (Ptr);
262 begin
263 Put (Standard_Error, Current_File_Name.all);
264 Put (Standard_Error, ':');
265 Put (Standard_Error, L (2 .. L'Length));
266 Put (Standard_Error, ':');
267 Put (Standard_Error, C (2 .. C'Length));
268 Put (Standard_Error, ": ");
270 Put_Line (Standard_Error, Msg);
271 Num_Errors := Num_Errors + 1;
272 end Error;
274 --------------------
275 -- Eval_Condition --
276 --------------------
278 function Eval_Condition
279 (Parenthesis : Natural := 0;
280 Do_Eval : Boolean := True)
281 return Boolean
283 Symbol_Is_True : Boolean := False; -- init to avoid warning
284 K : Keyword;
286 begin
287 -- Find the next subexpression
289 K := Scan_Keyword;
291 case K is
292 when K_None =>
293 Symbol_Is_True := Eval_Symbol (Do_Eval);
295 when K_Not =>
297 -- Not applies to the next subexpression (either a simple
298 -- evaluation like A or A'Defined, or a parenthesis expression)
300 K := Scan_Keyword;
302 if K = K_Open_Paren then
303 Symbol_Is_True := not Eval_Condition (Parenthesis + 1, Do_Eval);
305 elsif K = K_None then
306 Symbol_Is_True := not Eval_Symbol (Do_Eval);
308 else
309 Ptr := Start_Sym; -- Puts the keyword back
310 end if;
312 when K_Open_Paren =>
313 Symbol_Is_True := Eval_Condition (Parenthesis + 1, Do_Eval);
315 when others =>
316 Ptr := Start_Sym;
317 Error ("invalid syntax in preprocessor line");
318 raise Expression_Error;
319 end case;
321 -- Do we have a compound expression with AND, OR, ...
323 K := Scan_Keyword;
324 case K is
325 when K_None =>
326 if not At_End_Of_Line then
327 Error ("Invalid Syntax at end of line");
328 raise Expression_Error;
329 end if;
331 if Parenthesis /= 0 then
332 Error ("Unmatched opening parenthesis");
333 raise Expression_Error;
334 end if;
336 return Symbol_Is_True;
338 when K_Then =>
339 if Parenthesis /= 0 then
340 Error ("Unmatched opening parenthesis");
341 raise Expression_Error;
342 end if;
344 return Symbol_Is_True;
346 when K_Close_Paren =>
347 if Parenthesis = 0 then
348 Error ("Unmatched closing parenthesis");
349 raise Expression_Error;
350 end if;
352 return Symbol_Is_True;
354 when K_And =>
355 return Symbol_Is_True and Eval_Condition (Parenthesis, Do_Eval);
357 when K_Andthen =>
358 if not Symbol_Is_True then
360 -- Just skip the symbols for the remaining part
362 Symbol_Is_True := Eval_Condition (Parenthesis, False);
363 return False;
365 else
366 return Eval_Condition (Parenthesis, Do_Eval);
367 end if;
369 when K_Or =>
370 return Symbol_Is_True or Eval_Condition (Parenthesis, Do_Eval);
372 when K_Orelse =>
373 if Symbol_Is_True then
375 -- Just skip the symbols for the remaining part
377 Symbol_Is_True := Eval_Condition (Parenthesis, False);
378 return True;
380 else
381 return Eval_Condition (Parenthesis, Do_Eval);
382 end if;
384 when others =>
385 Error ("invalid syntax in preprocessor line");
386 raise Expression_Error;
387 end case;
389 end Eval_Condition;
391 -----------------
392 -- Eval_Symbol --
393 -----------------
395 function Eval_Symbol (Do_Eval : Boolean) return Boolean is
396 Sym : constant String := Line_Buffer (Start_Sym .. End_Sym);
397 K : Keyword;
398 Index : Natural;
399 Symbol_Defined : Boolean := False;
400 Symbol_Is_True : Boolean := False;
402 begin
403 -- Read the symbol
405 Skip_Spaces;
406 Start_Sym := Ptr;
408 if not Symbol_Scanned then
409 Error ("invalid symbol name");
410 raise Expression_Error;
411 end if;
413 Ptr := End_Sym + 1;
415 -- Test if we have a simple test (A) or a more complicated one
416 -- (A'Defined)
418 K := Scan_Keyword;
420 if K /= K_Defined and then K /= K_Equal then
421 Ptr := Start_Sym; -- Puts the keyword back
422 end if;
424 Index := Variable_Index (Sym);
426 case K is
427 when K_Defined =>
428 Symbol_Defined := Index /= Natural'Last;
429 Symbol_Is_True := Symbol_Defined;
431 when K_Equal =>
433 -- Read the second part of the statement
435 Skip_Spaces;
436 Start_Sym := Ptr;
438 if not Symbol_Scanned
439 and then End_Sym < Start_Sym
440 then
441 Error ("No right part for the equality test");
442 raise Expression_Error;
443 end if;
445 Ptr := End_Sym + 1;
447 -- If the variable was not found
449 if Do_Eval then
450 if Index = Natural'Last then
451 if not Undefined_Is_False then
452 Error ("symbol name """ & Sym &
453 """ is not defined in definitions file");
454 end if;
456 else
457 declare
458 Right : constant String
459 := Line_Buffer (Start_Sym .. End_Sym);
460 Index_R : Natural;
461 begin
462 if Right (Right'First) = '"' then
463 Symbol_Is_True :=
464 Matching_Strings
465 (Values (Index).all,
466 Right (Right'First + 1 .. Right'Last - 1));
467 else
468 Index_R := Variable_Index (Right);
469 if Index_R = Natural'Last then
470 Error ("Variable " & Right & " in test is "
471 & "not defined");
472 raise Expression_Error;
473 else
474 Symbol_Is_True :=
475 Matching_Strings (Values (Index).all,
476 Values (Index_R).all);
477 end if;
478 end if;
479 end;
480 end if;
481 end if;
483 when others =>
485 if Index = Natural'Last then
487 Symbol_Defined := False;
488 if Do_Eval and then not Symbol_Defined then
489 if Undefined_Is_False then
490 Symbol_Defined := True;
491 Symbol_Is_True := False;
493 else
494 Error
495 ("symbol name """ & Sym &
496 """ is not defined in definitions file");
497 end if;
498 end if;
500 elsif not Do_Eval then
501 Symbol_Is_True := True;
503 elsif Matching_Strings (Values (Index).all, "True") then
504 Symbol_Is_True := True;
506 elsif Matching_Strings (Values (Index).all, "False") then
507 Symbol_Is_True := False;
509 else
510 Error ("symbol value is not True or False");
511 Symbol_Is_True := False;
512 end if;
514 end case;
516 return Symbol_Is_True;
517 end Eval_Symbol;
519 ---------------
520 -- Help_Page --
521 ---------------
523 procedure Help_Page is
524 begin
525 Put_Line (Standard_Error,
526 "GNAT Preprocessor " &
527 Gnatvsn.Gnat_Version_String &
528 " Copyright 1996-2002 Free Software Foundation, Inc.");
529 Put_Line (Standard_Error,
530 "Usage: gnatprep [-bcrsu] [-Dsymbol=value] infile " &
531 "outfile [deffile]");
532 New_Line (Standard_Error);
533 Put_Line (Standard_Error, " infile Name of the input file");
534 Put_Line (Standard_Error, " outfile Name of the output file");
535 Put_Line (Standard_Error, " deffile Name of the definition file");
536 New_Line (Standard_Error);
537 Put_Line (Standard_Error, "gnatprep switches:");
538 Put_Line (Standard_Error, " -b Replace preprocessor lines by " &
539 "blank lines");
540 Put_Line (Standard_Error, " -c Keep preprocessor lines as comments");
541 Put_Line (Standard_Error, " -D Associate symbol with value");
542 Put_Line (Standard_Error, " -r Generate Source_Reference pragma");
543 Put_Line (Standard_Error, " -s Print a sorted list of symbol names " &
544 "and values");
545 Put_Line (Standard_Error, " -u Treat undefined symbols as FALSE");
546 New_Line (Standard_Error);
547 end Help_Page;
549 -----------
550 -- Image --
551 -----------
553 function Image (N : Natural) return String is
554 Result : constant String := Natural'Image (N);
555 begin
556 return Result (Result'First + 1 .. Result'Last);
557 end Image;
559 --------------------------
560 -- Is_Preprocessor_Line --
561 --------------------------
563 function Is_Preprocessor_Line return Boolean is
564 begin
565 Ptr := 1;
567 while Ptr <= Line_Length loop
568 if Line_Buffer (Ptr) = '#' then
569 Ptr := Ptr + 1;
570 return True;
572 elsif Line_Buffer (Ptr) > ' ' then
573 return False;
575 else
576 Ptr := Ptr + 1;
577 end if;
578 end loop;
580 return False;
581 end Is_Preprocessor_Line;
583 ----------------------
584 -- Matching_Strings --
585 ----------------------
587 function Matching_Strings (S1, S2 : String) return Boolean is
588 S2_Index : Integer := S2'First;
590 begin
591 for S1_Index in S1'Range loop
593 if To_Upper (S1 (S1_Index)) /= To_Upper (S2 (S2_Index)) then
594 return False;
596 else
597 if S2 (S2_Index) = '"'
598 and then S2_Index < S2'Last
599 and then S2 (S2_Index + 1) = '"'
600 then
601 S2_Index := S2_Index + 2;
602 else
603 S2_Index := S2_Index + 1;
604 end if;
606 -- If S2 was too short then
608 if S2_Index > S2'Last and then S1_Index < S1'Last then
609 return False;
610 end if;
611 end if;
612 end loop;
614 return S2_Index = S2'Last + 1;
615 end Matching_Strings;
617 -------------
618 -- No_Junk --
619 -------------
621 procedure No_Junk is
622 begin
623 Skip_Spaces;
625 if Ptr = Line_Length
626 or else (Ptr < Line_Length
627 and then Line_Buffer (Ptr .. Ptr + 1) /= "--")
628 then
629 Error ("extraneous text on preprocessor line ignored");
630 end if;
631 end No_Junk;
633 -------------------
634 -- OK_Identifier --
635 -------------------
637 function OK_Identifier (S : String) return Boolean is
638 P : Natural := S'First;
640 begin
641 if S'Length /= 0 and then S (P) = Character'Val (39) then -- '''
642 P := P + 1;
643 end if;
645 if S'Length = 0
646 or else not Is_Letter (S (P))
647 then
648 return False;
650 else
651 while P <= S'Last loop
652 if Is_Letter (S (P)) or Is_Digit (S (P)) then
653 null;
655 elsif S (P) = '_'
656 and then P < S'Last
657 and then S (P + 1) /= '_'
658 then
659 null;
661 else
662 return False;
663 end if;
665 P := P + 1;
666 end loop;
668 return True;
669 end if;
670 end OK_Identifier;
672 --------------------
673 -- Parse_Def_File --
674 --------------------
676 procedure Parse_Def_File is
677 begin
678 Open (Deffile, In_File, Deffile_Name.all);
680 -- Initialize data for procedure Error
682 Infile.Line_Num := 0;
683 Current_File_Name := Deffile_Name;
685 -- Loop through lines in symbol definitions file
687 while not End_Of_File (Deffile) loop
688 Get_Line (Deffile, Line_Buffer, Line_Length);
689 Infile.Line_Num := Infile.Line_Num + 1;
691 Ptr := 1;
692 Skip_Spaces;
694 if Ptr > Line_Length
695 or else (Ptr < Line_Length
696 and then
697 Line_Buffer (Ptr .. Ptr + 1) = "--")
698 then
699 goto Continue;
700 end if;
702 Start_Sym := Ptr;
704 if not Symbol_Scanned then
705 Error ("invalid symbol identifier """ &
706 Line_Buffer (Start_Sym .. End_Sym) &
707 '"');
708 goto Continue;
709 end if;
711 Ptr := End_Sym + 1;
712 Skip_Spaces;
714 if Ptr >= Line_Length
715 or else Line_Buffer (Ptr .. Ptr + 1) /= ":="
716 then
717 Error ("missing "":="" in symbol definition line");
718 goto Continue;
719 end if;
721 Ptr := Ptr + 2;
722 Skip_Spaces;
724 Num_Syms := Num_Syms + 1;
725 Symbols (Num_Syms) := new String'(Line_Buffer (Start_Sym .. End_Sym));
727 Start_Sym := Ptr;
728 End_Sym := Ptr - 1;
730 if At_End_Of_Line then
731 null;
733 elsif Line_Buffer (Start_Sym) = '"' then
734 End_Sym := End_Sym + 1;
735 loop
736 End_Sym := End_Sym + 1;
738 if End_Sym > Line_Length then
739 Error ("no closing quote for string constant");
740 goto Continue;
742 elsif End_Sym < Line_Length
743 and then Line_Buffer (End_Sym .. End_Sym + 1) = """"""
744 then
745 End_Sym := End_Sym + 1;
747 elsif Line_Buffer (End_Sym) = '"' then
748 exit;
749 end if;
750 end loop;
752 else
753 End_Sym := Ptr - 1;
755 while End_Sym < Line_Length
756 and then (Is_Alphanumeric (Line_Buffer (End_Sym + 1))
757 or else
758 Line_Buffer (End_Sym + 1) = '_'
759 or else
760 Line_Buffer (End_Sym + 1) = '.')
761 loop
762 End_Sym := End_Sym + 1;
763 end loop;
765 Ptr := End_Sym + 1;
767 if not At_End_Of_Line then
768 Error ("incorrect symbol value syntax");
769 goto Continue;
770 end if;
771 end if;
773 Values (Num_Syms) := new String'(Line_Buffer (Start_Sym .. End_Sym));
775 <<Continue>>
776 null;
777 end loop;
779 exception
780 -- Could not open the file
782 when Name_Error =>
783 Put_Line (Standard_Error, "cannot open " & Deffile_Name.all);
784 raise Fatal_Error;
785 end Parse_Def_File;
787 ------------------
788 -- Scan_Keyword --
789 ------------------
791 function Scan_Keyword return Keyword is
792 Kptr : constant Natural := Ptr;
794 begin
795 Skip_Spaces;
796 Start_Sym := Ptr;
798 if Symbol_Scanned then
800 -- If the symbol was the last thing on the line, End_Sym will
801 -- point too far in Line_Buffer
803 if End_Sym > Line_Length then
804 End_Sym := Line_Length;
805 end if;
807 Ptr := End_Sym + 1;
809 declare
810 Sym : constant String := Line_Buffer (Start_Sym .. End_Sym);
812 begin
813 if Matching_Strings (Sym, "not") then
814 return K_Not;
816 elsif Matching_Strings (Sym, "then") then
817 return K_Then;
819 elsif Matching_Strings (Sym, "if") then
820 return K_If;
822 elsif Matching_Strings (Sym, "else") then
823 return K_Else;
825 elsif Matching_Strings (Sym, "end") then
826 return K_End;
828 elsif Matching_Strings (Sym, "elsif") then
829 return K_Elsif;
831 elsif Matching_Strings (Sym, "and") then
832 if Scan_Keyword = K_Then then
833 Start_Sym := Kptr;
834 return K_Andthen;
835 else
836 Ptr := Start_Sym; -- Put back the last keyword read
837 Start_Sym := Kptr;
838 return K_And;
839 end if;
841 elsif Matching_Strings (Sym, "or") then
842 if Scan_Keyword = K_Else then
843 Start_Sym := Kptr;
844 return K_Orelse;
845 else
846 Ptr := Start_Sym; -- Put back the last keyword read
847 Start_Sym := Kptr;
848 return K_Or;
849 end if;
851 elsif Matching_Strings (Sym, "'defined") then
852 return K_Defined;
854 elsif Matching_Strings (Sym, "include") then
855 return K_Include;
857 elsif Sym = "(" then
858 return K_Open_Paren;
860 elsif Sym = ")" then
861 return K_Close_Paren;
863 elsif Sym = "=" then
864 return K_Equal;
865 end if;
866 end;
867 end if;
869 Ptr := Kptr;
870 return K_None;
871 end Scan_Keyword;
873 -----------------
874 -- Skip_Spaces --
875 -----------------
877 procedure Skip_Spaces is
878 begin
879 while Ptr <= Line_Length loop
880 if Line_Buffer (Ptr) /= ' '
881 and then Line_Buffer (Ptr) /= ASCII.HT
882 then
883 return;
884 else
885 Ptr := Ptr + 1;
886 end if;
887 end loop;
888 end Skip_Spaces;
890 --------------------
891 -- Symbol_Scanned --
892 --------------------
894 function Symbol_Scanned return Boolean is
895 begin
896 End_Sym := Start_Sym - 1;
898 case Line_Buffer (End_Sym + 1) is
900 when '(' | ')' | '=' =>
901 End_Sym := End_Sym + 1;
902 return True;
904 when '"' =>
905 End_Sym := End_Sym + 1;
906 while End_Sym < Line_Length loop
908 if Line_Buffer (End_Sym + 1) = '"' then
910 if End_Sym + 2 < Line_Length
911 and then Line_Buffer (End_Sym + 2) = '"'
912 then
913 End_Sym := End_Sym + 2;
914 else
915 exit;
916 end if;
917 else
918 End_Sym := End_Sym + 1;
919 end if;
920 end loop;
922 if End_Sym >= Line_Length then
923 Error ("Invalid string ");
924 raise Expression_Error;
925 end if;
927 End_Sym := End_Sym + 1;
928 return False;
930 when ''' =>
931 End_Sym := End_Sym + 1;
933 when others =>
934 null;
935 end case;
937 while End_Sym < Line_Length
938 and then (Is_Alphanumeric (Line_Buffer (End_Sym + 1))
939 or else Line_Buffer (End_Sym + 1) = '_')
940 loop
941 End_Sym := End_Sym + 1;
942 end loop;
944 return OK_Identifier (Line_Buffer (Start_Sym .. End_Sym));
945 end Symbol_Scanned;
947 --------------------
948 -- Variable_Index --
949 --------------------
951 function Variable_Index (Name : String) return Natural is
952 begin
953 for J in 1 .. Num_Syms loop
954 if Matching_Strings (Symbols (J).all, Name) then
955 return J;
956 end if;
957 end loop;
959 return Natural'Last;
960 end Variable_Index;
962 -- Start of processing for GNATprep
964 begin
966 -- Parse the switches
968 loop
969 case GNAT.Command_Line.Getopt ("D: b c r s u") is
970 when ASCII.NUL =>
971 exit;
973 when 'D' =>
974 declare
975 S : String := GNAT.Command_Line.Parameter;
976 Index : Natural;
978 begin
979 Index := Ada.Strings.Fixed.Index (S, "=");
981 if Index = 0 then
982 Num_Syms := Num_Syms + 1;
983 Symbols (Num_Syms) := new String'(S);
984 Values (Num_Syms) := new String'("True");
986 else
987 Num_Syms := Num_Syms + 1;
988 Symbols (Num_Syms) := new String'(S (S'First .. Index - 1));
989 Values (Num_Syms) := new String'(S (Index + 1 .. S'Last));
990 end if;
991 end;
993 when 'b' =>
994 Blank_Deleted_Lines := True;
996 when 'c' =>
997 Opt_Comment_Deleted_Lines := True;
999 when 'r' =>
1000 Source_Ref_Pragma := True;
1002 when 's' =>
1003 List_Symbols := True;
1005 when 'u' =>
1006 Undefined_Is_False := True;
1008 when others =>
1009 raise Usage_Error;
1010 end case;
1011 end loop;
1013 -- Get the file names
1015 loop
1016 declare
1017 S : constant String := GNAT.Command_Line.Get_Argument;
1019 begin
1020 exit when S'Length = 0;
1022 if Infile.Name = null then
1023 Infile.Name := new String'(S);
1024 elsif Outfile_Name = null then
1025 Outfile_Name := new String'(S);
1026 elsif Deffile_Name = null then
1027 Deffile_Name := new String'(S);
1028 else
1029 raise Usage_Error;
1030 end if;
1031 end;
1032 end loop;
1034 -- Test we had all the arguments needed
1036 if Infile.Name = null
1037 or else Outfile_Name = null
1038 then
1039 raise Usage_Error;
1040 end if;
1042 if Source_Ref_Pragma and (not Opt_Comment_Deleted_Lines) then
1043 Blank_Deleted_Lines := True;
1044 end if;
1046 -- Get symbol definitions
1048 if Deffile_Name /= null then
1049 Parse_Def_File;
1050 end if;
1052 if Num_Errors > 0 then
1053 raise Fatal_Error;
1055 elsif List_Symbols and then Num_Syms > 0 then
1056 List_Symbols_Case : declare
1058 function Lt (Op1, Op2 : Natural) return Boolean;
1059 -- Comparison routine for sort call
1061 procedure Move (From : Natural; To : Natural);
1062 -- Move routine for sort call
1064 function Lt (Op1, Op2 : Natural) return Boolean is
1065 L1 : constant Natural := Symbols (Op1)'Length;
1066 L2 : constant Natural := Symbols (Op2)'Length;
1067 MinL : constant Natural := Natural'Min (L1, L2);
1069 C1, C2 : Character;
1071 begin
1072 for J in 0 .. MinL - 1 loop
1073 C1 := To_Upper (Symbols (Op1).all (Symbols (Op1)'First + J));
1074 C2 := To_Upper (Symbols (Op2).all (Symbols (Op2)'First + J));
1076 if C1 < C2 then
1077 return True;
1079 elsif C1 > C2 then
1080 return False;
1081 end if;
1082 end loop;
1084 return L1 < L2;
1085 end Lt;
1087 procedure Move (From : Natural; To : Natural) is
1088 begin
1089 Symbols (To) := Symbols (From);
1090 Values (To) := Values (From);
1091 end Move;
1093 package Sort_Syms is new GNAT.Heap_Sort_G (Move, Lt);
1095 Max_L : Natural;
1096 -- Maximum length of any symbol
1098 -- Start of processing for List_Symbols_Case
1100 begin
1101 Sort_Syms.Sort (Num_Syms);
1103 Max_L := 7;
1104 for J in 1 .. Num_Syms loop
1105 Max_L := Natural'Max (Max_L, Symbols (J)'Length);
1106 end loop;
1108 New_Line;
1109 Put ("Symbol");
1111 for J in 1 .. Max_L - 5 loop
1112 Put (' ');
1113 end loop;
1115 Put_Line ("Value");
1117 Put ("------");
1119 for J in 1 .. Max_L - 5 loop
1120 Put (' ');
1121 end loop;
1123 Put_Line ("------");
1125 for J in 1 .. Num_Syms loop
1126 Put (Symbols (J).all);
1128 for K in 1 .. Max_L - Symbols (J)'Length + 1 loop
1129 Put (' ');
1130 end loop;
1132 Put_Line (Values (J).all);
1133 end loop;
1135 New_Line;
1136 end List_Symbols_Case;
1137 end if;
1139 -- Open files and initialize preprocessing
1141 begin
1142 Open (Infile.File, In_File, Infile.Name.all);
1144 exception
1145 when Name_Error =>
1146 Put_Line (Standard_Error, "cannot open " & Infile.Name.all);
1147 raise Fatal_Error;
1148 end;
1150 begin
1151 Create (Outfile, Out_File, Outfile_Name.all);
1153 exception
1154 when Name_Error =>
1155 Put_Line (Standard_Error, "cannot create " & Outfile_Name.all);
1156 raise Fatal_Error;
1157 end;
1159 Infile.Line_Num := 0;
1160 Current_File_Name := Infile.Name;
1162 PP_Depth := 0;
1163 PP (0).Deleting := False;
1165 -- We return here after we start reading an include file and after
1166 -- we have finished reading an include file.
1168 <<Read_In_File>>
1170 -- If we generate Source_Reference pragmas, then generate one
1171 -- either with line number 1 for a newly included file, or
1172 -- with the number of the next line when we have returned to the
1173 -- including file.
1175 if Source_Ref_Pragma then
1176 Put_Line
1177 (Outfile, "pragma Source_Reference (" &
1178 Image (Infile.Line_Num + 1) &
1179 ", """ & Infile.Name.all & """);");
1180 end if;
1182 -- Loop through lines in input file
1184 while not End_Of_File (Infile.File) loop
1185 Get_Line (Infile.File, Line_Buffer, Line_Length);
1186 Infile.Line_Num := Infile.Line_Num + 1;
1188 -- Handle preprocessor line
1190 if Is_Preprocessor_Line then
1191 K := Scan_Keyword;
1193 case K is
1195 -- Include file
1197 when K_Include =>
1198 -- Ignore if Deleting is True
1200 if PP (PP_Depth).Deleting then
1201 goto Output;
1202 end if;
1204 Skip_Spaces;
1206 if Ptr >= Line_Length then
1207 Error ("no file to include");
1209 elsif Line_Buffer (Ptr) /= '"' then
1210 Error
1211 ("file to include must be specified as a literal string");
1213 else
1214 declare
1215 Start_File : constant Positive := Ptr + 1;
1217 begin
1218 Ptr := Line_Length;
1220 while Line_Buffer (Ptr) = ' '
1221 or else Line_Buffer (Ptr) = ASCII.HT
1222 loop
1223 Ptr := Ptr - 1;
1224 end loop;
1226 if Ptr <= Start_File
1227 or else Line_Buffer (Ptr) /= '"'
1228 then
1229 Error ("no string literal for included file");
1231 else
1232 if Infile.Next = null then
1233 Infile.Next := new Input;
1234 Infile.Next.Prev := Infile;
1235 end if;
1237 Infile := Infile.Next;
1238 Infile.Name :=
1239 new String'(Line_Buffer (Start_File .. Ptr - 1));
1241 -- Check for circularity: an file including itself,
1242 -- either directly or indirectly.
1244 declare
1245 File : Input_Ptr := Infile.Prev;
1247 begin
1248 while File /= null
1249 and then File.Name.all /= Infile.Name.all
1250 loop
1251 File := File.Prev;
1252 end loop;
1254 if File /= null then
1255 Infile := Infile.Prev;
1256 Error ("circularity in included files");
1258 while File.Prev /= null loop
1259 File := File.Prev;
1260 end loop;
1262 while File /= Infile.Next loop
1263 Error ('"' & File.Name.all &
1264 """ includes """ &
1265 File.Next.Name.all & '"');
1266 File := File.Next;
1267 end loop;
1269 else
1270 -- We have a file name and no circularity.
1271 -- Open the file and record an error if the
1272 -- file cannot be opened.
1274 begin
1275 Open (Infile.File, In_File, Infile.Name.all);
1276 Current_File_Name := Infile.Name;
1277 Infile.Line_Num := 0;
1279 -- If we use Source_Reference pragma,
1280 -- we need to output one for this new file.
1281 goto Read_In_File;
1283 exception
1284 when Name_Error =>
1286 -- We need to set the input file to
1287 -- the including file, so that the
1288 -- line number is correct when reporting
1289 -- the error.
1291 Infile := Infile.Prev;
1292 Error ("cannot open """ &
1293 Infile.Next.Name.all & '"');
1294 end;
1295 end if;
1296 end;
1297 end if;
1298 end;
1299 end if;
1301 -- If/Elsif processing
1303 when K_If | K_Elsif =>
1305 -- If differs from elsif only in that an initial stack entry
1306 -- must be made for the new if range. We set the match seen
1307 -- entry to a copy of the deleting status in the range above
1308 -- us. If we are deleting in the range above us, then we want
1309 -- all the branches of the nested #if to delete.
1311 if K = K_If then
1312 PP_Depth := PP_Depth + 1;
1313 PP (PP_Depth) :=
1314 (If_Line => Infile.Line_Num,
1315 If_Name => Infile.Name,
1316 Else_Line => 0,
1317 Deleting => False,
1318 Match_Seen => PP (PP_Depth - 1).Deleting);
1320 elsif PP_Depth = 0 then
1321 Error ("no matching #if for this #elsif");
1322 goto Output;
1324 end if;
1326 PP (PP_Depth).Deleting := True;
1328 if not PP (PP_Depth).Match_Seen
1329 and then Eval_Condition = True
1330 then
1332 -- Case of match and no match yet in this #if
1334 PP (PP_Depth).Deleting := False;
1335 PP (PP_Depth).Match_Seen := True;
1336 No_Junk;
1337 end if;
1339 -- Processing for #else
1341 when K_Else =>
1343 if PP_Depth = 0 then
1344 Error ("no matching #if for this #else");
1346 elsif PP (PP_Depth).Else_Line /= 0 then
1347 Error ("duplicate #else line (previous was on line" &
1348 Natural'Image (PP (PP_Depth).Else_Line) &
1349 ")");
1351 else
1352 PP (PP_Depth).Else_Line := Infile.Line_Num;
1353 PP (PP_Depth).Deleting := PP (PP_Depth).Match_Seen;
1354 end if;
1356 No_Junk;
1358 -- Process for #end
1360 when K_End =>
1362 if PP_Depth = 0 then
1363 Error ("no matching #if for this #end");
1365 else
1366 Skip_Spaces;
1368 if Scan_Keyword /= K_If then
1369 Error ("expected if after #end");
1370 Ptr := Line_Length + 1;
1371 end if;
1373 Skip_Spaces;
1375 if Ptr > Line_Length
1376 or else Line_Buffer (Ptr) /= ';'
1377 then
1378 Error ("missing semicolon after #end if");
1379 else
1380 Ptr := Ptr + 1;
1381 end if;
1383 No_Junk;
1385 PP_Depth := PP_Depth - 1;
1386 end if;
1388 when others =>
1389 Error ("invalid preprocessor keyword syntax");
1391 end case;
1393 -- Handle symbol substitution
1395 -- Substitution is not allowed in string (which we simply skip),
1396 -- but is allowed inside character constants. The last case is
1397 -- because there is no way to know whether the user want to
1398 -- substitute the name of an attribute ('Min or 'Max for instance)
1399 -- or actually meant to substitue a character ('$name' is probably
1400 -- a character constant, but my_type'$name'Min is probably an
1401 -- attribute, with $name=Base)
1403 else
1404 Ptr := 1;
1406 while Ptr < Line_Length loop
1407 exit when At_End_Of_Line;
1409 case Line_Buffer (Ptr) is
1411 when ''' =>
1413 -- Two special cases here:
1414 -- '"' => we don't want the " sign to appear as belonging
1415 -- to a string.
1416 -- '$' => this is obviously not a substitution, just skip it
1418 if Ptr < Line_Length - 1
1419 and then Line_Buffer (Ptr + 1) = '"'
1420 then
1421 Ptr := Ptr + 2;
1422 elsif Ptr < Line_Length - 2
1423 and then Line_Buffer (Ptr + 1 .. Ptr + 2) = "$'"
1424 then
1425 Ptr := Ptr + 2;
1426 end if;
1428 when '"' =>
1430 -- The special case of "" inside the string is easy to
1431 -- handle: just ignore them. The second one will be seen
1432 -- as the beginning of a second string
1434 Ptr := Ptr + 1;
1435 while Ptr < Line_Length
1436 and then Line_Buffer (Ptr) /= '"'
1437 loop
1438 Ptr := Ptr + 1;
1439 end loop;
1441 when '$' =>
1443 -- $ found, so scan out possible following symbol
1445 Start_Sym := Ptr + 1;
1447 if Symbol_Scanned then
1449 -- Look up symbol in table and if found do replacement
1451 for J in 1 .. Num_Syms loop
1452 if Matching_Strings
1453 (Symbols (J).all, Line_Buffer (Start_Sym .. End_Sym))
1454 then
1455 declare
1456 OldL : constant Positive :=
1457 End_Sym - Start_Sym + 2;
1458 NewL : constant Positive := Values (J)'Length;
1459 AdjL : constant Integer := NewL - OldL;
1460 NewP : constant Positive := Ptr + NewL - 1;
1462 begin
1463 Line_Buffer (NewP + 1 .. Line_Length + AdjL) :=
1464 Line_Buffer (End_Sym + 1 .. Line_Length);
1465 Line_Buffer (Ptr .. NewP) := Values (J).all;
1467 Ptr := NewP;
1468 Line_Length := Line_Length + AdjL;
1469 end;
1471 exit;
1472 end if;
1473 end loop;
1474 end if;
1476 when others =>
1477 null;
1479 end case;
1480 Ptr := Ptr + 1;
1481 end loop;
1482 end if;
1484 -- Here after dealing with preprocessor line, output current line
1486 <<Output>>
1488 if Is_Preprocessor_Line or else PP (PP_Depth).Deleting then
1489 if Blank_Deleted_Lines then
1490 New_Line (Outfile);
1492 elsif Opt_Comment_Deleted_Lines then
1493 if Line_Length = 0 then
1494 Put_Line (Outfile, "--!");
1495 else
1496 Put (Outfile, "--! ");
1497 Put_Line (Outfile, Line_Buffer (1 .. Line_Length));
1498 end if;
1499 end if;
1501 else
1502 Put_Line (Outfile, Line_Buffer (1 .. Line_Length));
1503 end if;
1504 end loop;
1506 -- If we have finished reading an included file, close it and continue
1507 -- with the next line of the including file.
1509 if Infile.Prev /= null then
1510 Close (Infile.File);
1511 Infile := Infile.Prev;
1512 Current_File_Name := Infile.Name;
1513 goto Read_In_File;
1514 end if;
1516 for J in 1 .. PP_Depth loop
1517 if PP (J).If_Name = Infile.Name then
1518 Error ("no matching #end for #if at line" &
1519 Natural'Image (PP (J).If_Line));
1520 else
1521 Error ("no matching #end for #if at line" &
1522 Natural'Image (PP (J).If_Line) &
1523 " of file """ & PP (J).If_Name.all & '"');
1524 end if;
1525 end loop;
1527 if Num_Errors = 0 then
1528 Close (Outfile);
1529 Set_Exit_Status (0);
1530 else
1531 Delete (Outfile);
1532 Set_Exit_Status (1);
1533 end if;
1535 exception
1536 when Usage_Error =>
1537 Help_Page;
1538 Set_Exit_Status (1);
1540 when GNAT.Command_Line.Invalid_Parameter =>
1541 Put_Line (Standard_Error, "No parameter given for -"
1542 & GNAT.Command_Line.Full_Switch);
1543 Help_Page;
1544 Set_Exit_Status (1);
1546 when GNAT.Command_Line.Invalid_Switch =>
1547 Put_Line (Standard_Error, "Invalid Switch: -"
1548 & GNAT.Command_Line.Full_Switch);
1549 Help_Page;
1550 Set_Exit_Status (1);
1552 when Fatal_Error =>
1553 Set_Exit_Status (1);
1555 when Expression_Error =>
1556 Set_Exit_Status (1);
1558 end GNATprep;