* gcc.c-torture/execute/20020307-1.c: New test.
[official-gcc.git] / gcc / ada / gnatprep.adb
blobccff6fc4a3cae1b8ec21d4bde1240271e6e76382
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT COMPILER COMPONENTS --
4 -- --
5 -- G N A T P R E P --
6 -- --
7 -- B o d y --
8 -- --
9 -- $Revision: 1.27 $
10 -- --
11 -- Copyright (C) 1996-2001, Free Software Foundation, Inc. --
12 -- --
13 -- GNAT is free software; you can redistribute it and/or modify it under --
14 -- terms of the GNU General Public License as published by the Free Soft- --
15 -- ware Foundation; either version 2, or (at your option) any later ver- --
16 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
17 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
18 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
19 -- for more details. You should have received a copy of the GNU General --
20 -- Public License distributed with GNAT; see file COPYING. If not, write --
21 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
22 -- MA 02111-1307, USA. --
23 -- --
24 -- GNAT was originally developed by the GNAT team at New York University. --
25 -- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). --
26 -- --
27 ------------------------------------------------------------------------------
29 with Ada.Characters.Handling; use Ada.Characters.Handling;
30 with Ada.Strings.Fixed;
31 with Ada.Command_Line; use Ada.Command_Line;
32 with Ada.Text_IO; use Ada.Text_IO;
34 with GNAT.Heap_Sort_G;
35 with GNAT.Command_Line;
37 with Gnatvsn;
39 procedure GNATprep is
40 pragma Ident (Gnatvsn.Gnat_Version_String);
42 Version_String : constant String := "$Revision: 1.27 $";
44 type Strptr is access String;
46 Usage_Error : exception;
47 -- Raised if a usage error is detected, causes termination of processing
48 -- with an appropriate error message and error exit status set.
50 Fatal_Error : exception;
51 -- Exception raised if fatal error detected
53 Expression_Error : exception;
54 -- Exception raised when an invalid boolean expression is found
55 -- on a preprocessor line
57 ------------------------
58 -- Argument Line Data --
59 ------------------------
61 Infile_Name : Strptr;
62 Outfile_Name : Strptr;
63 Deffile_Name : Strptr;
64 -- Names of files
66 Infile : File_Type;
67 Outfile : File_Type;
68 Deffile : File_Type;
70 Opt_Comment_Deleted_Lines : Boolean := False; -- Set if -c switch set
71 Blank_Deleted_Lines : Boolean := False; -- Set if -b switch set
72 List_Symbols : Boolean := False; -- Set if -s switch set
73 Source_Ref_Pragma : Boolean := False; -- Set if -r switch set
74 Undefined_Is_False : Boolean := False; -- Set if -u switch set
75 -- Record command line options
77 ---------------------------
78 -- Definitions File Data --
79 ---------------------------
81 Num_Syms : Natural := 0;
82 -- Number of symbols defined in definitions file
84 Symbols : array (0 .. 10_000) of Strptr;
85 Values : array (0 .. 10_000) of Strptr;
86 -- Symbol names and values. Note that the zero'th element is used only
87 -- during the call to Sort (to hold a temporary value, as required by
88 -- the GNAT.Heap_Sort_G interface).
90 ---------------------
91 -- Input File Data --
92 ---------------------
94 Current_File_Name : Strptr;
95 -- Holds name of file being read (definitions file or input file)
97 Line_Buffer : String (1 .. 20_000);
98 -- Hold one line
100 Line_Length : Natural;
101 -- Length of line in Line_Buffer
103 Line_Num : Natural;
104 -- Current input file line number
106 Ptr : Natural;
107 -- Input scan pointer for line in Line_Buffer
109 type Keyword is (K_Not, K_Then, K_If, K_Else, K_End, K_Elsif,
110 K_And, K_Or, K_Open_Paren, K_Close_Paren,
111 K_Defined, K_Andthen, K_Orelse, K_Equal, K_None);
112 -- Keywords that are recognized on preprocessor lines. K_None indicates
113 -- that no keyword was present.
115 K : Keyword;
116 -- Scanned keyword
118 Start_Sym, End_Sym : Natural;
119 -- First and last positions of scanned symbol
121 Num_Errors : Natural := 0;
122 -- Number of errors detected
124 -----------------------
125 -- Preprocessor Data --
126 -----------------------
128 -- The following record represents the state of an #if structure:
130 type PP_Rec is record
131 If_Line : Positive;
132 -- Line number for #if line
134 Else_Line : Natural;
135 -- Line number for #else line, zero = no else seen yet
137 Deleting : Boolean;
138 -- True if lines currently being deleted
140 Match_Seen : Boolean;
141 -- True if either the #if condition or one of the previously seen
142 -- #elsif lines was true, meaning that any future #elsif sections
143 -- or the #else section, is to be deleted.
144 end record;
146 PP_Depth : Natural;
147 -- Preprocessor #if nesting level. A value of zero means that we are
148 -- outside any #if structure.
150 PP : array (0 .. 100) of PP_Rec;
151 -- Stack of records showing state of #if structures. PP (1) is the
152 -- outer level entry, and PP (PP_Depth) is the active entry. PP (0)
153 -- contains a dummy entry whose Deleting flag is always set to False.
155 -----------------
156 -- Subprograms --
157 -----------------
159 function At_End_Of_Line return Boolean;
160 -- First advances Ptr using Skip_Spaces. Then returns True if Ptr is
161 -- either at the end of the line, or at a -- comment sequence.
163 procedure Error (Msg : String);
164 -- Post error message with given text. The line number is taken from
165 -- Line_Num, and the column number from Ptr.
167 function Eval_Condition
168 (Parenthesis : Natural := 0;
169 Do_Eval : Boolean := True)
170 return Boolean;
171 -- Eval the condition found in the current Line. The condition can
172 -- include any of the 'and', 'or', 'not', and parenthesis subexpressions.
173 -- If Line is an invalid expression, then Expression_Error is raised,
174 -- after an error message has been printed. Line can include 'then'
175 -- followed by a comment, which is automatically ignored. If Do_Eval
176 -- is False, then the expression is not evaluated at all, and symbols
177 -- are just skipped.
179 function Eval_Symbol (Do_Eval : Boolean) return Boolean;
180 -- Read and evaluate the next symbol or expression (A, A'Defined, A=...)
181 -- If it is followed by 'Defined or an equality test, read as many symbols
182 -- as needed. Do_Eval has the same meaning as in Eval_Condition
184 procedure Help_Page;
185 -- Print a help page to summarize the usage of gnatprep
187 function Is_Preprocessor_Line return Boolean;
188 -- Tests if current line is a preprocessor line, i.e. that its first
189 -- non-blank character is a # character. If so, then a result of True
190 -- is returned, and Ptr is set to point to the character following the
191 -- # character. If not, False is returned and Ptr is undefined.
193 procedure No_Junk;
194 -- Make sure no junk is present on a preprocessor line. Ptr points past
195 -- the scanned preprocessor syntax.
197 function OK_Identifier (S : String) return Boolean;
198 -- Tests if given referenced string is valid Ada identifier
200 function Matching_Strings (S1, S2 : String) return Boolean;
201 -- Check if S1 and S2 are the same string (this is a case independent
202 -- comparison, lower and upper case letters are considered to match).
203 -- Duplicate quotes in S2 are considered as a single quote ("" => ")
205 procedure Parse_Def_File;
206 -- Parse the deffile given by the user
208 function Scan_Keyword return Keyword;
209 -- Advances Ptr to end of line or next non-blank using Skip_Spaces. Then
210 -- attempts to scan out a recognized keyword. if a recognized keyword is
211 -- found, sets Ptr past it, and returns the code for the keyword, if not,
212 -- then Ptr is left unchanged pointing to a non-blank character or to the
213 -- end of the line.
215 function Symbol_Scanned return Boolean;
216 -- On entry, Start_Sym is set to the first character of an identifier
217 -- symbol to be scanned out. On return, End_Sym is set to the last
218 -- character of the identifier, and the result indicates if the scanned
219 -- symbol is a valid identifier (True = valid). Ptr is not changed.
221 procedure Skip_Spaces;
222 -- Skips Ptr past tabs and spaces to next non-blank, or one character
223 -- past the end of line.
225 function Variable_Index (Name : String) return Natural;
226 -- Returns the index of the variable in the table. If the variable is not
227 -- found, returns Natural'Last
229 --------------------
230 -- At_End_Of_Line --
231 --------------------
233 function At_End_Of_Line return Boolean is
234 begin
235 Skip_Spaces;
237 return Ptr > Line_Length
238 or else
239 (Ptr < Line_Length and then Line_Buffer (Ptr .. Ptr + 1) = "--");
240 end At_End_Of_Line;
242 -----------
243 -- Error --
244 -----------
246 procedure Error (Msg : String) is
247 L : constant String := Natural'Image (Line_Num);
248 C : constant String := Natural'Image (Ptr);
250 begin
251 Put (Standard_Error, Current_File_Name.all);
252 Put (Standard_Error, ':');
253 Put (Standard_Error, L (2 .. L'Length));
254 Put (Standard_Error, ':');
255 Put (Standard_Error, C (2 .. C'Length));
256 Put (Standard_Error, ": ");
258 Put_Line (Standard_Error, Msg);
259 Num_Errors := Num_Errors + 1;
260 end Error;
262 --------------------
263 -- Eval_Condition --
264 --------------------
266 function Eval_Condition
267 (Parenthesis : Natural := 0;
268 Do_Eval : Boolean := True)
269 return Boolean
271 Symbol_Is_True : Boolean := False; -- init to avoid warning
272 K : Keyword;
274 begin
275 -- Find the next subexpression
277 K := Scan_Keyword;
279 case K is
280 when K_None =>
281 Symbol_Is_True := Eval_Symbol (Do_Eval);
283 when K_Not =>
285 -- Not applies to the next subexpression (either a simple
286 -- evaluation like A or A'Defined, or a parenthesis expression)
288 K := Scan_Keyword;
290 if K = K_Open_Paren then
291 Symbol_Is_True := not Eval_Condition (Parenthesis + 1, Do_Eval);
293 elsif K = K_None then
294 Symbol_Is_True := not Eval_Symbol (Do_Eval);
296 else
297 Ptr := Start_Sym; -- Puts the keyword back
298 end if;
300 when K_Open_Paren =>
301 Symbol_Is_True := Eval_Condition (Parenthesis + 1, Do_Eval);
303 when others =>
304 Ptr := Start_Sym;
305 Error ("invalid syntax in preprocessor line");
306 raise Expression_Error;
307 end case;
309 -- Do we have a compound expression with AND, OR, ...
311 K := Scan_Keyword;
312 case K is
313 when K_None =>
314 if not At_End_Of_Line then
315 Error ("Invalid Syntax at end of line");
316 raise Expression_Error;
317 end if;
319 if Parenthesis /= 0 then
320 Error ("Unmatched opening parenthesis");
321 raise Expression_Error;
322 end if;
324 return Symbol_Is_True;
326 when K_Then =>
327 if Parenthesis /= 0 then
328 Error ("Unmatched opening parenthesis");
329 raise Expression_Error;
330 end if;
332 return Symbol_Is_True;
334 when K_Close_Paren =>
335 if Parenthesis = 0 then
336 Error ("Unmatched closing parenthesis");
337 raise Expression_Error;
338 end if;
340 return Symbol_Is_True;
342 when K_And =>
343 return Symbol_Is_True and Eval_Condition (Parenthesis, Do_Eval);
345 when K_Andthen =>
346 if not Symbol_Is_True then
348 -- Just skip the symbols for the remaining part
350 Symbol_Is_True := Eval_Condition (Parenthesis, False);
351 return False;
353 else
354 return Eval_Condition (Parenthesis, Do_Eval);
355 end if;
357 when K_Or =>
358 return Symbol_Is_True or Eval_Condition (Parenthesis, Do_Eval);
360 when K_Orelse =>
361 if Symbol_Is_True then
363 -- Just skip the symbols for the remaining part
365 Symbol_Is_True := Eval_Condition (Parenthesis, False);
366 return True;
368 else
369 return Eval_Condition (Parenthesis, Do_Eval);
370 end if;
372 when others =>
373 Error ("invalid syntax in preprocessor line");
374 raise Expression_Error;
375 end case;
377 end Eval_Condition;
379 -----------------
380 -- Eval_Symbol --
381 -----------------
383 function Eval_Symbol (Do_Eval : Boolean) return Boolean is
384 Sym : constant String := Line_Buffer (Start_Sym .. End_Sym);
385 K : Keyword;
386 Index : Natural;
387 Symbol_Defined : Boolean := False;
388 Symbol_Is_True : Boolean := False;
390 begin
391 -- Read the symbol
393 Skip_Spaces;
394 Start_Sym := Ptr;
396 if not Symbol_Scanned then
397 Error ("invalid symbol name");
398 raise Expression_Error;
399 end if;
401 Ptr := End_Sym + 1;
403 -- Test if we have a simple test (A) or a more complicated one
404 -- (A'Defined)
406 K := Scan_Keyword;
408 if K /= K_Defined and then K /= K_Equal then
409 Ptr := Start_Sym; -- Puts the keyword back
410 end if;
412 Index := Variable_Index (Sym);
414 case K is
415 when K_Defined =>
416 Symbol_Defined := Index /= Natural'Last;
417 Symbol_Is_True := Symbol_Defined;
419 when K_Equal =>
421 -- Read the second part of the statement
422 Skip_Spaces;
423 Start_Sym := Ptr;
425 if not Symbol_Scanned
426 and then End_Sym < Start_Sym
427 then
428 Error ("No right part for the equality test");
429 raise Expression_Error;
430 end if;
432 Ptr := End_Sym + 1;
434 -- If the variable was not found
436 if Do_Eval then
437 if Index = Natural'Last then
438 if not Undefined_Is_False then
439 Error ("symbol name """ & Sym &
440 """ is not defined in definitions file");
441 end if;
443 else
444 declare
445 Right : constant String
446 := Line_Buffer (Start_Sym .. End_Sym);
447 Index_R : Natural;
448 begin
449 if Right (Right'First) = '"' then
450 Symbol_Is_True :=
451 Matching_Strings
452 (Values (Index).all,
453 Right (Right'First + 1 .. Right'Last - 1));
454 else
455 Index_R := Variable_Index (Right);
456 if Index_R = Natural'Last then
457 Error ("Variable " & Right & " in test is "
458 & "not defined");
459 raise Expression_Error;
460 else
461 Symbol_Is_True :=
462 Matching_Strings (Values (Index).all,
463 Values (Index_R).all);
464 end if;
465 end if;
466 end;
467 end if;
468 end if;
470 when others =>
472 if Index = Natural'Last then
474 Symbol_Defined := False;
475 if Do_Eval and then not Symbol_Defined then
476 if Undefined_Is_False then
477 Symbol_Defined := True;
478 Symbol_Is_True := False;
480 else
481 Error
482 ("symbol name """ & Sym &
483 """ is not defined in definitions file");
484 end if;
485 end if;
487 elsif not Do_Eval then
488 Symbol_Is_True := True;
490 elsif Matching_Strings (Values (Index).all, "True") then
491 Symbol_Is_True := True;
493 elsif Matching_Strings (Values (Index).all, "False") then
494 Symbol_Is_True := False;
496 else
497 Error ("symbol value is not True or False");
498 Symbol_Is_True := False;
499 end if;
501 end case;
503 return Symbol_Is_True;
504 end Eval_Symbol;
506 ---------------
507 -- Help_Page --
508 ---------------
510 procedure Help_Page is
511 begin
512 Put_Line (Standard_Error,
513 "GNAT Preprocessor Version " &
514 Version_String (12 .. 15) &
515 " Copyright 1996-2001 Free Software Foundation, Inc.");
516 Put_Line (Standard_Error,
517 "Usage: gnatprep [-bcrsu] [-Dsymbol=value] infile " &
518 "outfile [deffile]");
519 New_Line (Standard_Error);
520 Put_Line (Standard_Error, " infile Name of the input file");
521 Put_Line (Standard_Error, " outfile Name of the output file");
522 Put_Line (Standard_Error, " deffile Name of the definition file");
523 New_Line (Standard_Error);
524 Put_Line (Standard_Error, "gnatprep switches:");
525 Put_Line (Standard_Error, " -b Replace preprocessor lines by " &
526 "blank lines");
527 Put_Line (Standard_Error, " -c Keep preprocessor lines as comments");
528 Put_Line (Standard_Error, " -D Associate symbol with value");
529 Put_Line (Standard_Error, " -r Generate Source_Reference pragma");
530 Put_Line (Standard_Error, " -s Print a sorted list of symbol names " &
531 "and values");
532 Put_Line (Standard_Error, " -u Treat undefined symbols as FALSE");
533 New_Line (Standard_Error);
534 end Help_Page;
536 --------------------------
537 -- Is_Preprocessor_Line --
538 --------------------------
540 function Is_Preprocessor_Line return Boolean is
541 begin
542 Ptr := 1;
544 while Ptr <= Line_Length loop
545 if Line_Buffer (Ptr) = '#' then
546 Ptr := Ptr + 1;
547 return True;
549 elsif Line_Buffer (Ptr) > ' ' then
550 return False;
552 else
553 Ptr := Ptr + 1;
554 end if;
555 end loop;
557 return False;
558 end Is_Preprocessor_Line;
560 ----------------------
561 -- Matching_Strings --
562 ----------------------
564 function Matching_Strings (S1, S2 : String) return Boolean is
565 S2_Index : Integer := S2'First;
567 begin
568 for S1_Index in S1'Range loop
570 if To_Upper (S1 (S1_Index)) /= To_Upper (S2 (S2_Index)) then
571 return False;
573 else
574 if S2 (S2_Index) = '"'
575 and then S2_Index < S2'Last
576 and then S2 (S2_Index + 1) = '"'
577 then
578 S2_Index := S2_Index + 2;
579 else
580 S2_Index := S2_Index + 1;
581 end if;
583 -- If S2 was too short then
585 if S2_Index > S2'Last and then S1_Index < S1'Last then
586 return False;
587 end if;
588 end if;
589 end loop;
591 return S2_Index = S2'Last + 1;
592 end Matching_Strings;
594 -------------
595 -- No_Junk --
596 -------------
598 procedure No_Junk is
599 begin
600 Skip_Spaces;
602 if Ptr = Line_Length
603 or else (Ptr < Line_Length
604 and then Line_Buffer (Ptr .. Ptr + 1) /= "--")
605 then
606 Error ("extraneous text on preprocessor line ignored");
607 end if;
608 end No_Junk;
610 -------------------
611 -- OK_Identifier --
612 -------------------
614 function OK_Identifier (S : String) return Boolean is
615 P : Natural := S'First;
617 begin
618 if S'Length /= 0 and then S (P) = Character'Val (39) then -- '''
619 P := P + 1;
620 end if;
622 if S'Length = 0
623 or else not Is_Letter (S (P))
624 then
625 return False;
627 else
628 while P <= S'Last loop
629 if Is_Letter (S (P)) or Is_Digit (S (P)) then
630 null;
632 elsif S (P) = '_'
633 and then P < S'Last
634 and then S (P + 1) /= '_'
635 then
636 null;
638 else
639 return False;
640 end if;
642 P := P + 1;
643 end loop;
645 return True;
646 end if;
647 end OK_Identifier;
649 --------------------
650 -- Parse_Def_File --
651 --------------------
653 procedure Parse_Def_File is
654 begin
655 Open (Deffile, In_File, Deffile_Name.all);
657 Line_Num := 0;
658 Current_File_Name := Deffile_Name;
660 -- Loop through lines in symbol definitions file
662 while not End_Of_File (Deffile) loop
663 Get_Line (Deffile, Line_Buffer, Line_Length);
664 Line_Num := Line_Num + 1;
666 Ptr := 1;
667 Skip_Spaces;
669 if Ptr > Line_Length
670 or else (Ptr < Line_Length
671 and then
672 Line_Buffer (Ptr .. Ptr + 1) = "--")
673 then
674 goto Continue;
675 end if;
677 Start_Sym := Ptr;
679 if not Symbol_Scanned then
680 Error ("invalid symbol identifier """ &
681 Line_Buffer (Start_Sym .. End_Sym) &
682 '"');
683 goto Continue;
684 end if;
686 Ptr := End_Sym + 1;
687 Skip_Spaces;
689 if Ptr >= Line_Length
690 or else Line_Buffer (Ptr .. Ptr + 1) /= ":="
691 then
692 Error ("missing "":="" in symbol definition line");
693 goto Continue;
694 end if;
696 Ptr := Ptr + 2;
697 Skip_Spaces;
699 Num_Syms := Num_Syms + 1;
700 Symbols (Num_Syms) := new String'(Line_Buffer (Start_Sym .. End_Sym));
702 Start_Sym := Ptr;
703 End_Sym := Ptr - 1;
705 if At_End_Of_Line then
706 null;
708 elsif Line_Buffer (Start_Sym) = '"' then
709 End_Sym := End_Sym + 1;
710 loop
711 End_Sym := End_Sym + 1;
713 if End_Sym > Line_Length then
714 Error ("no closing quote for string constant");
715 goto Continue;
717 elsif End_Sym < Line_Length
718 and then Line_Buffer (End_Sym .. End_Sym + 1) = """"""
719 then
720 End_Sym := End_Sym + 1;
722 elsif Line_Buffer (End_Sym) = '"' then
723 exit;
724 end if;
725 end loop;
727 else
728 End_Sym := Ptr - 1;
730 while End_Sym < Line_Length
731 and then (Is_Alphanumeric (Line_Buffer (End_Sym + 1))
732 or else
733 Line_Buffer (End_Sym + 1) = '_'
734 or else
735 Line_Buffer (End_Sym + 1) = '.')
736 loop
737 End_Sym := End_Sym + 1;
738 end loop;
740 Ptr := End_Sym + 1;
742 if not At_End_Of_Line then
743 Error ("incorrect symbol value syntax");
744 goto Continue;
745 end if;
746 end if;
748 Values (Num_Syms) := new String'(Line_Buffer (Start_Sym .. End_Sym));
750 <<Continue>>
751 null;
752 end loop;
754 exception
755 -- Could not open the file
757 when Name_Error =>
758 Put_Line (Standard_Error, "cannot open " & Deffile_Name.all);
759 raise Fatal_Error;
760 end Parse_Def_File;
762 ------------------
763 -- Scan_Keyword --
764 ------------------
766 function Scan_Keyword return Keyword is
767 Kptr : constant Natural := Ptr;
769 begin
770 Skip_Spaces;
771 Start_Sym := Ptr;
773 if Symbol_Scanned then
775 -- If the symbol was the last thing on the line, End_Sym will
776 -- point too far in Line_Buffer
778 if End_Sym > Line_Length then
779 End_Sym := Line_Length;
780 end if;
782 Ptr := End_Sym + 1;
784 declare
785 Sym : constant String := Line_Buffer (Start_Sym .. End_Sym);
787 begin
788 if Matching_Strings (Sym, "not") then
789 return K_Not;
791 elsif Matching_Strings (Sym, "then") then
792 return K_Then;
794 elsif Matching_Strings (Sym, "if") then
795 return K_If;
797 elsif Matching_Strings (Sym, "else") then
798 return K_Else;
800 elsif Matching_Strings (Sym, "end") then
801 return K_End;
803 elsif Matching_Strings (Sym, "elsif") then
804 return K_Elsif;
806 elsif Matching_Strings (Sym, "and") then
807 if Scan_Keyword = K_Then then
808 Start_Sym := Kptr;
809 return K_Andthen;
810 else
811 Ptr := Start_Sym; -- Put back the last keyword read
812 Start_Sym := Kptr;
813 return K_And;
814 end if;
816 elsif Matching_Strings (Sym, "or") then
817 if Scan_Keyword = K_Else then
818 Start_Sym := Kptr;
819 return K_Orelse;
820 else
821 Ptr := Start_Sym; -- Put back the last keyword read
822 Start_Sym := Kptr;
823 return K_Or;
824 end if;
826 elsif Matching_Strings (Sym, "'defined") then
827 return K_Defined;
829 elsif Sym = "(" then
830 return K_Open_Paren;
832 elsif Sym = ")" then
833 return K_Close_Paren;
835 elsif Sym = "=" then
836 return K_Equal;
837 end if;
838 end;
839 end if;
841 Ptr := Kptr;
842 return K_None;
843 end Scan_Keyword;
845 -----------------
846 -- Skip_Spaces --
847 -----------------
849 procedure Skip_Spaces is
850 begin
851 while Ptr <= Line_Length loop
852 if Line_Buffer (Ptr) /= ' '
853 and then Line_Buffer (Ptr) /= ASCII.HT
854 then
855 return;
856 else
857 Ptr := Ptr + 1;
858 end if;
859 end loop;
860 end Skip_Spaces;
862 --------------------
863 -- Symbol_Scanned --
864 --------------------
866 function Symbol_Scanned return Boolean is
867 begin
868 End_Sym := Start_Sym - 1;
870 case Line_Buffer (End_Sym + 1) is
872 when '(' | ')' | '=' =>
873 End_Sym := End_Sym + 1;
874 return True;
876 when '"' =>
877 End_Sym := End_Sym + 1;
878 while End_Sym < Line_Length loop
880 if Line_Buffer (End_Sym + 1) = '"' then
882 if End_Sym + 2 < Line_Length
883 and then Line_Buffer (End_Sym + 2) = '"'
884 then
885 End_Sym := End_Sym + 2;
886 else
887 exit;
888 end if;
889 else
890 End_Sym := End_Sym + 1;
891 end if;
892 end loop;
894 if End_Sym >= Line_Length then
895 Error ("Invalid string ");
896 raise Expression_Error;
897 end if;
899 End_Sym := End_Sym + 1;
900 return False;
902 when ''' =>
903 End_Sym := End_Sym + 1;
905 when others =>
906 null;
907 end case;
909 while End_Sym < Line_Length
910 and then (Is_Alphanumeric (Line_Buffer (End_Sym + 1))
911 or else Line_Buffer (End_Sym + 1) = '_')
912 loop
913 End_Sym := End_Sym + 1;
914 end loop;
916 return OK_Identifier (Line_Buffer (Start_Sym .. End_Sym));
917 end Symbol_Scanned;
919 --------------------
920 -- Variable_Index --
921 --------------------
923 function Variable_Index (Name : String) return Natural is
924 begin
925 for J in 1 .. Num_Syms loop
926 if Matching_Strings (Symbols (J).all, Name) then
927 return J;
928 end if;
929 end loop;
931 return Natural'Last;
932 end Variable_Index;
934 -- Start of processing for GNATprep
936 begin
938 -- Parse the switches
940 loop
941 case GNAT.Command_Line.Getopt ("D: b c r s u") is
942 when ASCII.NUL =>
943 exit;
945 when 'D' =>
946 declare
947 S : String := GNAT.Command_Line.Parameter;
948 Index : Natural;
950 begin
951 Index := Ada.Strings.Fixed.Index (S, "=");
953 if Index = 0 then
954 Num_Syms := Num_Syms + 1;
955 Symbols (Num_Syms) := new String'(S);
956 Values (Num_Syms) := new String'("True");
958 else
959 Num_Syms := Num_Syms + 1;
960 Symbols (Num_Syms) := new String'(S (S'First .. Index - 1));
961 Values (Num_Syms) := new String'(S (Index + 1 .. S'Last));
962 end if;
963 end;
965 when 'b' =>
966 Blank_Deleted_Lines := True;
968 when 'c' =>
969 Opt_Comment_Deleted_Lines := True;
971 when 'r' =>
972 Source_Ref_Pragma := True;
974 when 's' =>
975 List_Symbols := True;
977 when 'u' =>
978 Undefined_Is_False := True;
980 when others =>
981 raise Usage_Error;
982 end case;
983 end loop;
985 -- Get the file names
987 loop
988 declare
989 S : constant String := GNAT.Command_Line.Get_Argument;
991 begin
992 exit when S'Length = 0;
994 if Infile_Name = null then
995 Infile_Name := new String'(S);
996 elsif Outfile_Name = null then
997 Outfile_Name := new String'(S);
998 elsif Deffile_Name = null then
999 Deffile_Name := new String'(S);
1000 else
1001 raise Usage_Error;
1002 end if;
1003 end;
1004 end loop;
1006 -- Test we had all the arguments needed
1008 if Infile_Name = null
1009 or else Outfile_Name = null
1010 then
1011 raise Usage_Error;
1012 end if;
1014 if Source_Ref_Pragma and (not Opt_Comment_Deleted_Lines) then
1015 Blank_Deleted_Lines := True;
1016 end if;
1018 -- Get symbol definitions
1020 if Deffile_Name /= null then
1021 Parse_Def_File;
1022 end if;
1024 if Num_Errors > 0 then
1025 raise Fatal_Error;
1027 elsif List_Symbols and then Num_Syms > 0 then
1028 List_Symbols_Case : declare
1030 function Lt (Op1, Op2 : Natural) return Boolean;
1031 -- Comparison routine for sort call
1033 procedure Move (From : Natural; To : Natural);
1034 -- Move routine for sort call
1036 function Lt (Op1, Op2 : Natural) return Boolean is
1037 L1 : constant Natural := Symbols (Op1)'Length;
1038 L2 : constant Natural := Symbols (Op2)'Length;
1039 MinL : constant Natural := Natural'Min (L1, L2);
1041 C1, C2 : Character;
1043 begin
1044 for J in 0 .. MinL - 1 loop
1045 C1 := To_Upper (Symbols (Op1).all (Symbols (Op1)'First + J));
1046 C2 := To_Upper (Symbols (Op2).all (Symbols (Op2)'First + J));
1048 if C1 < C2 then
1049 return True;
1051 elsif C1 > C2 then
1052 return False;
1053 end if;
1054 end loop;
1056 return L1 < L2;
1057 end Lt;
1059 procedure Move (From : Natural; To : Natural) is
1060 begin
1061 Symbols (To) := Symbols (From);
1062 Values (To) := Values (From);
1063 end Move;
1065 package Sort_Syms is new GNAT.Heap_Sort_G (Move, Lt);
1067 Max_L : Natural;
1068 -- Maximum length of any symbol
1070 -- Start of processing for List_Symbols_Case
1072 begin
1073 Sort_Syms.Sort (Num_Syms);
1075 Max_L := 7;
1076 for J in 1 .. Num_Syms loop
1077 Max_L := Natural'Max (Max_L, Symbols (J)'Length);
1078 end loop;
1080 New_Line;
1081 Put ("Symbol");
1083 for J in 1 .. Max_L - 5 loop
1084 Put (' ');
1085 end loop;
1087 Put_Line ("Value");
1089 Put ("------");
1091 for J in 1 .. Max_L - 5 loop
1092 Put (' ');
1093 end loop;
1095 Put_Line ("------");
1097 for J in 1 .. Num_Syms loop
1098 Put (Symbols (J).all);
1100 for K in 1 .. Max_L - Symbols (J)'Length + 1 loop
1101 Put (' ');
1102 end loop;
1104 Put_Line (Values (J).all);
1105 end loop;
1107 New_Line;
1108 end List_Symbols_Case;
1109 end if;
1111 -- Open files and initialize preprocessing
1113 begin
1114 Open (Infile, In_File, Infile_Name.all);
1116 exception
1117 when Name_Error =>
1118 Put_Line (Standard_Error, "cannot open " & Infile_Name.all);
1119 raise Fatal_Error;
1120 end;
1122 begin
1123 Create (Outfile, Out_File, Outfile_Name.all);
1125 exception
1126 when Name_Error =>
1127 Put_Line (Standard_Error, "cannot create " & Outfile_Name.all);
1128 raise Fatal_Error;
1129 end;
1131 if Source_Ref_Pragma then
1132 Put_Line
1133 (Outfile, "pragma Source_Reference (1, """ & Infile_Name.all & """);");
1134 end if;
1136 Line_Num := 0;
1137 Current_File_Name := Infile_Name;
1139 PP_Depth := 0;
1140 PP (0).Deleting := False;
1142 -- Loop through lines in input file
1144 while not End_Of_File (Infile) loop
1145 Get_Line (Infile, Line_Buffer, Line_Length);
1146 Line_Num := Line_Num + 1;
1148 -- Handle preprocessor line
1150 if Is_Preprocessor_Line then
1151 K := Scan_Keyword;
1153 case K is
1155 -- If/Elsif processing
1157 when K_If | K_Elsif =>
1159 -- If differs from elsif only in that an initial stack entry
1160 -- must be made for the new if range. We set the match seen
1161 -- entry to a copy of the deleting status in the range above
1162 -- us. If we are deleting in the range above us, then we want
1163 -- all the branches of the nested #if to delete.
1165 if K = K_If then
1166 PP_Depth := PP_Depth + 1;
1167 PP (PP_Depth) :=
1168 (If_Line => Line_Num,
1169 Else_Line => 0,
1170 Deleting => False,
1171 Match_Seen => PP (PP_Depth - 1).Deleting);
1173 elsif PP_Depth = 0 then
1174 Error ("no matching #if for this #elsif");
1175 goto Output;
1177 end if;
1179 PP (PP_Depth).Deleting := True;
1181 if not PP (PP_Depth).Match_Seen
1182 and then Eval_Condition = True
1183 then
1185 -- Case of match and no match yet in this #if
1187 PP (PP_Depth).Deleting := False;
1188 PP (PP_Depth).Match_Seen := True;
1189 No_Junk;
1190 end if;
1192 -- Processing for #else
1194 when K_Else =>
1196 if PP_Depth = 0 then
1197 Error ("no matching #if for this #else");
1199 elsif PP (PP_Depth).Else_Line /= 0 then
1200 Error ("duplicate #else line (previous was on line" &
1201 Natural'Image (PP (PP_Depth).Else_Line) &
1202 ")");
1204 else
1205 PP (PP_Depth).Else_Line := Line_Num;
1206 PP (PP_Depth).Deleting := PP (PP_Depth).Match_Seen;
1207 end if;
1209 No_Junk;
1211 -- Process for #end
1213 when K_End =>
1215 if PP_Depth = 0 then
1216 Error ("no matching #if for this #end");
1218 else
1219 Skip_Spaces;
1221 if Scan_Keyword /= K_If then
1222 Error ("expected if after #end");
1223 Ptr := Line_Length + 1;
1224 end if;
1226 Skip_Spaces;
1228 if Ptr > Line_Length
1229 or else Line_Buffer (Ptr) /= ';'
1230 then
1231 Error ("missing semicolon after #end if");
1232 else
1233 Ptr := Ptr + 1;
1234 end if;
1236 No_Junk;
1238 PP_Depth := PP_Depth - 1;
1239 end if;
1241 when others =>
1242 Error ("invalid preprocessor keyword syntax");
1244 end case;
1246 -- Handle symbol substitution
1248 -- Substitution is not allowed in string (which we simply skip),
1249 -- but is allowed inside character constants. The last case is
1250 -- because there is no way to know whether the user want to
1251 -- substitute the name of an attribute ('Min or 'Max for instance)
1252 -- or actually meant to substitue a character ('$name' is probably
1253 -- a character constant, but my_type'$name'Min is probably an
1254 -- attribute, with $name=Base)
1256 else
1257 Ptr := 1;
1259 while Ptr < Line_Length loop
1260 exit when At_End_Of_Line;
1262 case Line_Buffer (Ptr) is
1264 when ''' =>
1266 -- Two special cases here:
1267 -- '"' => we don't want the " sign to appear as belonging
1268 -- to a string.
1269 -- '$' => this is obviously not a substitution, just skip it
1271 if Ptr < Line_Length - 1
1272 and then Line_Buffer (Ptr + 1) = '"'
1273 then
1274 Ptr := Ptr + 2;
1275 elsif Ptr < Line_Length - 2
1276 and then Line_Buffer (Ptr + 1 .. Ptr + 2) = "$'"
1277 then
1278 Ptr := Ptr + 2;
1279 end if;
1281 when '"' =>
1283 -- The special case of "" inside the string is easy to
1284 -- handle: just ignore them. The second one will be seen
1285 -- as the beginning of a second string
1287 Ptr := Ptr + 1;
1288 while Ptr < Line_Length
1289 and then Line_Buffer (Ptr) /= '"'
1290 loop
1291 Ptr := Ptr + 1;
1292 end loop;
1294 when '$' =>
1296 -- $ found, so scan out possible following symbol
1298 Start_Sym := Ptr + 1;
1300 if Symbol_Scanned then
1302 -- Look up symbol in table and if found do replacement
1304 for J in 1 .. Num_Syms loop
1305 if Matching_Strings
1306 (Symbols (J).all, Line_Buffer (Start_Sym .. End_Sym))
1307 then
1308 declare
1309 OldL : constant Positive :=
1310 End_Sym - Start_Sym + 2;
1311 NewL : constant Positive := Values (J)'Length;
1312 AdjL : constant Integer := NewL - OldL;
1313 NewP : constant Positive := Ptr + NewL - 1;
1315 begin
1316 Line_Buffer (NewP + 1 .. Line_Length + AdjL) :=
1317 Line_Buffer (End_Sym + 1 .. Line_Length);
1318 Line_Buffer (Ptr .. NewP) := Values (J).all;
1320 Ptr := NewP;
1321 Line_Length := Line_Length + AdjL;
1322 end;
1324 exit;
1325 end if;
1326 end loop;
1327 end if;
1329 when others =>
1330 null;
1332 end case;
1333 Ptr := Ptr + 1;
1334 end loop;
1335 end if;
1337 -- Here after dealing with preprocessor line, output current line
1339 <<Output>>
1341 if Is_Preprocessor_Line or else PP (PP_Depth).Deleting then
1342 if Blank_Deleted_Lines then
1343 New_Line (Outfile);
1345 elsif Opt_Comment_Deleted_Lines then
1346 if Line_Length = 0 then
1347 Put_Line (Outfile, "--!");
1348 else
1349 Put (Outfile, "--! ");
1350 Put_Line (Outfile, Line_Buffer (1 .. Line_Length));
1351 end if;
1352 end if;
1354 else
1355 Put_Line (Outfile, Line_Buffer (1 .. Line_Length));
1356 end if;
1357 end loop;
1359 for J in 1 .. PP_Depth loop
1360 Error ("no matching #end for #if at line" &
1361 Natural'Image (PP (J).If_Line));
1362 end loop;
1364 if Num_Errors = 0 then
1365 Close (Outfile);
1366 Set_Exit_Status (0);
1367 else
1368 Delete (Outfile);
1369 Set_Exit_Status (1);
1370 end if;
1372 exception
1373 when Usage_Error =>
1374 Help_Page;
1375 Set_Exit_Status (1);
1377 when GNAT.Command_Line.Invalid_Parameter =>
1378 Put_Line (Standard_Error, "No parameter given for -"
1379 & GNAT.Command_Line.Full_Switch);
1380 Help_Page;
1381 Set_Exit_Status (1);
1383 when GNAT.Command_Line.Invalid_Switch =>
1384 Put_Line (Standard_Error, "Invalid Switch: -"
1385 & GNAT.Command_Line.Full_Switch);
1386 Help_Page;
1387 Set_Exit_Status (1);
1389 when Fatal_Error =>
1390 Set_Exit_Status (1);
1392 when Expression_Error =>
1393 Set_Exit_Status (1);
1395 end GNATprep;