* sh.h (REG_CLASS_FROM_LETTER): Change to:
[official-gcc.git] / gcc / ada / gnatprep.adb
blob2502db7cee24b61fb75ca1c85e6926d89fbdf482
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT COMPILER COMPONENTS --
4 -- --
5 -- G N A T P R E P --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 1996-2002, Free Software Foundation, Inc. --
10 -- --
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. --
21 -- --
22 -- GNAT was originally developed by the GNAT team at New York University. --
23 -- Extensive contributions were provided by Ada Core Technologies Inc. --
24 -- --
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;
35 with Gnatvsn;
37 procedure GNATprep is
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;
58 -- Names of files
60 type Input;
61 type Input_Ptr is access Input;
62 type Input is record
63 File : File_Type;
64 Next : Input_Ptr;
65 Prev : Input_Ptr;
66 Name : Strptr;
67 Line_Num : Natural := 0;
68 end record;
69 -- Data for the current input file (main input file or included file
70 -- or definition file).
72 Infile : Input_Ptr := new Input;
73 Outfile : File_Type;
74 Deffile : File_Type;
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).
96 ---------------------
97 -- Input File Data --
98 ---------------------
100 Current_File_Name : Strptr;
101 -- Holds name of file being read (definitions file or input file)
103 Line_Buffer : String (1 .. 20_000);
104 -- Hold one line
106 Line_Length : Natural;
107 -- Length of line in Line_Buffer
109 Ptr : Natural;
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,
115 K_None);
116 -- Keywords that are recognized on preprocessor lines. K_None indicates
117 -- that no keyword was present.
119 K : Keyword;
120 -- Scanned keyword
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
135 If_Line : Positive;
136 -- Line number for #if line
138 If_Name : Strptr;
139 -- File name of #if line
141 Else_Line : Natural;
142 -- Line number for #else line, zero = no else seen yet
144 Deleting : Boolean;
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.
152 end record;
154 PP_Depth : Natural;
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.
163 -----------------
164 -- Subprograms --
165 -----------------
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)
178 return Boolean;
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
185 -- are just skipped.
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
192 procedure Help_Page;
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.
204 procedure No_Junk;
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
224 -- end of the line.
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
240 --------------------
241 -- At_End_Of_Line --
242 --------------------
244 function At_End_Of_Line return Boolean is
245 begin
246 Skip_Spaces;
248 return Ptr > Line_Length
249 or else
250 (Ptr < Line_Length and then Line_Buffer (Ptr .. Ptr + 1) = "--");
251 end At_End_Of_Line;
253 -----------
254 -- Error --
255 -----------
257 procedure Error (Msg : String) is
258 L : constant String := Natural'Image (Infile.Line_Num);
259 C : constant String := Natural'Image (Ptr);
261 begin
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;
271 end Error;
273 --------------------
274 -- Eval_Condition --
275 --------------------
277 function Eval_Condition
278 (Parenthesis : Natural := 0;
279 Do_Eval : Boolean := True)
280 return Boolean
282 Symbol_Is_True : Boolean := False; -- init to avoid warning
283 K : Keyword;
285 begin
286 -- Find the next subexpression
288 K := Scan_Keyword;
290 case K is
291 when K_None =>
292 Symbol_Is_True := Eval_Symbol (Do_Eval);
294 when K_Not =>
296 -- Not applies to the next subexpression (either a simple
297 -- evaluation like A or A'Defined, or a parenthesis expression)
299 K := Scan_Keyword;
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);
307 else
308 Ptr := Start_Sym; -- Puts the keyword back
309 end if;
311 when K_Open_Paren =>
312 Symbol_Is_True := Eval_Condition (Parenthesis + 1, Do_Eval);
314 when others =>
315 Ptr := Start_Sym;
316 Error ("invalid syntax in preprocessor line");
317 raise Expression_Error;
318 end case;
320 -- Do we have a compound expression with AND, OR, ...
322 K := Scan_Keyword;
323 case K is
324 when K_None =>
325 if not At_End_Of_Line then
326 Error ("Invalid Syntax at end of line");
327 raise Expression_Error;
328 end if;
330 if Parenthesis /= 0 then
331 Error ("Unmatched opening parenthesis");
332 raise Expression_Error;
333 end if;
335 return Symbol_Is_True;
337 when K_Then =>
338 if Parenthesis /= 0 then
339 Error ("Unmatched opening parenthesis");
340 raise Expression_Error;
341 end if;
343 return Symbol_Is_True;
345 when K_Close_Paren =>
346 if Parenthesis = 0 then
347 Error ("Unmatched closing parenthesis");
348 raise Expression_Error;
349 end if;
351 return Symbol_Is_True;
353 when K_And =>
354 return Symbol_Is_True and Eval_Condition (Parenthesis, Do_Eval);
356 when K_Andthen =>
357 if not Symbol_Is_True then
359 -- Just skip the symbols for the remaining part
361 Symbol_Is_True := Eval_Condition (Parenthesis, False);
362 return False;
364 else
365 return Eval_Condition (Parenthesis, Do_Eval);
366 end if;
368 when K_Or =>
369 return Symbol_Is_True or Eval_Condition (Parenthesis, Do_Eval);
371 when K_Orelse =>
372 if Symbol_Is_True then
374 -- Just skip the symbols for the remaining part
376 Symbol_Is_True := Eval_Condition (Parenthesis, False);
377 return True;
379 else
380 return Eval_Condition (Parenthesis, Do_Eval);
381 end if;
383 when others =>
384 Error ("invalid syntax in preprocessor line");
385 raise Expression_Error;
386 end case;
388 end Eval_Condition;
390 -----------------
391 -- Eval_Symbol --
392 -----------------
394 function Eval_Symbol (Do_Eval : Boolean) return Boolean is
395 Sym : constant String := Line_Buffer (Start_Sym .. End_Sym);
396 K : Keyword;
397 Index : Natural;
398 Symbol_Defined : Boolean := False;
399 Symbol_Is_True : Boolean := False;
401 begin
402 -- Read the symbol
404 Skip_Spaces;
405 Start_Sym := Ptr;
407 if not Symbol_Scanned then
408 Error ("invalid symbol name");
409 raise Expression_Error;
410 end if;
412 Ptr := End_Sym + 1;
414 -- Test if we have a simple test (A) or a more complicated one
415 -- (A'Defined)
417 K := Scan_Keyword;
419 if K /= K_Defined and then K /= K_Equal then
420 Ptr := Start_Sym; -- Puts the keyword back
421 end if;
423 Index := Variable_Index (Sym);
425 case K is
426 when K_Defined =>
427 Symbol_Defined := Index /= Natural'Last;
428 Symbol_Is_True := Symbol_Defined;
430 when K_Equal =>
432 -- Read the second part of the statement
434 Skip_Spaces;
435 Start_Sym := Ptr;
437 if not Symbol_Scanned
438 and then End_Sym < Start_Sym
439 then
440 Error ("No right part for the equality test");
441 raise Expression_Error;
442 end if;
444 Ptr := End_Sym + 1;
446 -- If the variable was not found
448 if Do_Eval then
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");
453 end if;
455 else
456 declare
457 Right : constant String
458 := Line_Buffer (Start_Sym .. End_Sym);
459 Index_R : Natural;
460 begin
461 if Right (Right'First) = '"' then
462 Symbol_Is_True :=
463 Matching_Strings
464 (Values (Index).all,
465 Right (Right'First + 1 .. Right'Last - 1));
466 else
467 Index_R := Variable_Index (Right);
468 if Index_R = Natural'Last then
469 Error ("Variable " & Right & " in test is "
470 & "not defined");
471 raise Expression_Error;
472 else
473 Symbol_Is_True :=
474 Matching_Strings (Values (Index).all,
475 Values (Index_R).all);
476 end if;
477 end if;
478 end;
479 end if;
480 end if;
482 when others =>
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;
492 else
493 Error
494 ("symbol name """ & Sym &
495 """ is not defined in definitions file");
496 end if;
497 end if;
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;
508 else
509 Error ("symbol value is not True or False");
510 Symbol_Is_True := False;
511 end if;
513 end case;
515 return Symbol_Is_True;
516 end Eval_Symbol;
518 ---------------
519 -- Help_Page --
520 ---------------
522 procedure Help_Page is
523 begin
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 " &
538 "blank lines");
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 " &
543 "and values");
544 Put_Line (Standard_Error, " -u Treat undefined symbols as FALSE");
545 New_Line (Standard_Error);
546 end Help_Page;
548 -----------
549 -- Image --
550 -----------
552 function Image (N : Natural) return String is
553 Result : constant String := Natural'Image (N);
554 begin
555 return Result (Result'First + 1 .. Result'Last);
556 end Image;
558 --------------------------
559 -- Is_Preprocessor_Line --
560 --------------------------
562 function Is_Preprocessor_Line return Boolean is
563 begin
564 Ptr := 1;
566 while Ptr <= Line_Length loop
567 if Line_Buffer (Ptr) = '#' then
568 Ptr := Ptr + 1;
569 return True;
571 elsif Line_Buffer (Ptr) > ' ' then
572 return False;
574 else
575 Ptr := Ptr + 1;
576 end if;
577 end loop;
579 return False;
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;
589 begin
590 for S1_Index in S1'Range loop
592 if To_Upper (S1 (S1_Index)) /= To_Upper (S2 (S2_Index)) then
593 return False;
595 else
596 if S2 (S2_Index) = '"'
597 and then S2_Index < S2'Last
598 and then S2 (S2_Index + 1) = '"'
599 then
600 S2_Index := S2_Index + 2;
601 else
602 S2_Index := S2_Index + 1;
603 end if;
605 -- If S2 was too short then
607 if S2_Index > S2'Last and then S1_Index < S1'Last then
608 return False;
609 end if;
610 end if;
611 end loop;
613 return S2_Index = S2'Last + 1;
614 end Matching_Strings;
616 -------------
617 -- No_Junk --
618 -------------
620 procedure No_Junk is
621 begin
622 Skip_Spaces;
624 if Ptr = Line_Length
625 or else (Ptr < Line_Length
626 and then Line_Buffer (Ptr .. Ptr + 1) /= "--")
627 then
628 Error ("extraneous text on preprocessor line ignored");
629 end if;
630 end No_Junk;
632 -------------------
633 -- OK_Identifier --
634 -------------------
636 function OK_Identifier (S : String) return Boolean is
637 P : Natural := S'First;
639 begin
640 if S'Length /= 0 and then S (P) = Character'Val (39) then -- '''
641 P := P + 1;
642 end if;
644 if S'Length = 0
645 or else not Is_Letter (S (P))
646 then
647 return False;
649 else
650 while P <= S'Last loop
651 if Is_Letter (S (P)) or Is_Digit (S (P)) then
652 null;
654 elsif S (P) = '_'
655 and then P < S'Last
656 and then S (P + 1) /= '_'
657 then
658 null;
660 else
661 return False;
662 end if;
664 P := P + 1;
665 end loop;
667 return True;
668 end if;
669 end OK_Identifier;
671 --------------------
672 -- Parse_Def_File --
673 --------------------
675 procedure Parse_Def_File is
676 begin
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;
690 Ptr := 1;
691 Skip_Spaces;
693 if Ptr > Line_Length
694 or else (Ptr < Line_Length
695 and then
696 Line_Buffer (Ptr .. Ptr + 1) = "--")
697 then
698 goto Continue;
699 end if;
701 Start_Sym := Ptr;
703 if not Symbol_Scanned then
704 Error ("invalid symbol identifier """ &
705 Line_Buffer (Start_Sym .. End_Sym) &
706 '"');
707 goto Continue;
708 end if;
710 Ptr := End_Sym + 1;
711 Skip_Spaces;
713 if Ptr >= Line_Length
714 or else Line_Buffer (Ptr .. Ptr + 1) /= ":="
715 then
716 Error ("missing "":="" in symbol definition line");
717 goto Continue;
718 end if;
720 Ptr := Ptr + 2;
721 Skip_Spaces;
723 Num_Syms := Num_Syms + 1;
724 Symbols (Num_Syms) := new String'(Line_Buffer (Start_Sym .. End_Sym));
726 Start_Sym := Ptr;
727 End_Sym := Ptr - 1;
729 if At_End_Of_Line then
730 null;
732 elsif Line_Buffer (Start_Sym) = '"' then
733 End_Sym := End_Sym + 1;
734 loop
735 End_Sym := End_Sym + 1;
737 if End_Sym > Line_Length then
738 Error ("no closing quote for string constant");
739 goto Continue;
741 elsif End_Sym < Line_Length
742 and then Line_Buffer (End_Sym .. End_Sym + 1) = """"""
743 then
744 End_Sym := End_Sym + 1;
746 elsif Line_Buffer (End_Sym) = '"' then
747 exit;
748 end if;
749 end loop;
751 else
752 End_Sym := Ptr - 1;
754 while End_Sym < Line_Length
755 and then (Is_Alphanumeric (Line_Buffer (End_Sym + 1))
756 or else
757 Line_Buffer (End_Sym + 1) = '_'
758 or else
759 Line_Buffer (End_Sym + 1) = '.')
760 loop
761 End_Sym := End_Sym + 1;
762 end loop;
764 Ptr := End_Sym + 1;
766 if not At_End_Of_Line then
767 Error ("incorrect symbol value syntax");
768 goto Continue;
769 end if;
770 end if;
772 Values (Num_Syms) := new String'(Line_Buffer (Start_Sym .. End_Sym));
774 <<Continue>>
775 null;
776 end loop;
778 exception
779 -- Could not open the file
781 when Name_Error =>
782 Put_Line (Standard_Error, "cannot open " & Deffile_Name.all);
783 raise Fatal_Error;
784 end Parse_Def_File;
786 ------------------
787 -- Scan_Keyword --
788 ------------------
790 function Scan_Keyword return Keyword is
791 Kptr : constant Natural := Ptr;
793 begin
794 Skip_Spaces;
795 Start_Sym := 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;
804 end if;
806 Ptr := End_Sym + 1;
808 declare
809 Sym : constant String := Line_Buffer (Start_Sym .. End_Sym);
811 begin
812 if Matching_Strings (Sym, "not") then
813 return K_Not;
815 elsif Matching_Strings (Sym, "then") then
816 return K_Then;
818 elsif Matching_Strings (Sym, "if") then
819 return K_If;
821 elsif Matching_Strings (Sym, "else") then
822 return K_Else;
824 elsif Matching_Strings (Sym, "end") then
825 return K_End;
827 elsif Matching_Strings (Sym, "elsif") then
828 return K_Elsif;
830 elsif Matching_Strings (Sym, "and") then
831 if Scan_Keyword = K_Then then
832 Start_Sym := Kptr;
833 return K_Andthen;
834 else
835 Ptr := Start_Sym; -- Put back the last keyword read
836 Start_Sym := Kptr;
837 return K_And;
838 end if;
840 elsif Matching_Strings (Sym, "or") then
841 if Scan_Keyword = K_Else then
842 Start_Sym := Kptr;
843 return K_Orelse;
844 else
845 Ptr := Start_Sym; -- Put back the last keyword read
846 Start_Sym := Kptr;
847 return K_Or;
848 end if;
850 elsif Matching_Strings (Sym, "'defined") then
851 return K_Defined;
853 elsif Matching_Strings (Sym, "include") then
854 return K_Include;
856 elsif Sym = "(" then
857 return K_Open_Paren;
859 elsif Sym = ")" then
860 return K_Close_Paren;
862 elsif Sym = "=" then
863 return K_Equal;
864 end if;
865 end;
866 end if;
868 Ptr := Kptr;
869 return K_None;
870 end Scan_Keyword;
872 -----------------
873 -- Skip_Spaces --
874 -----------------
876 procedure Skip_Spaces is
877 begin
878 while Ptr <= Line_Length loop
879 if Line_Buffer (Ptr) /= ' '
880 and then Line_Buffer (Ptr) /= ASCII.HT
881 then
882 return;
883 else
884 Ptr := Ptr + 1;
885 end if;
886 end loop;
887 end Skip_Spaces;
889 --------------------
890 -- Symbol_Scanned --
891 --------------------
893 function Symbol_Scanned return Boolean is
894 begin
895 End_Sym := Start_Sym - 1;
897 case Line_Buffer (End_Sym + 1) is
899 when '(' | ')' | '=' =>
900 End_Sym := End_Sym + 1;
901 return True;
903 when '"' =>
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) = '"'
911 then
912 End_Sym := End_Sym + 2;
913 else
914 exit;
915 end if;
916 else
917 End_Sym := End_Sym + 1;
918 end if;
919 end loop;
921 if End_Sym >= Line_Length then
922 Error ("Invalid string ");
923 raise Expression_Error;
924 end if;
926 End_Sym := End_Sym + 1;
927 return False;
929 when ''' =>
930 End_Sym := End_Sym + 1;
932 when others =>
933 null;
934 end case;
936 while End_Sym < Line_Length
937 and then (Is_Alphanumeric (Line_Buffer (End_Sym + 1))
938 or else Line_Buffer (End_Sym + 1) = '_')
939 loop
940 End_Sym := End_Sym + 1;
941 end loop;
943 return OK_Identifier (Line_Buffer (Start_Sym .. End_Sym));
944 end Symbol_Scanned;
946 --------------------
947 -- Variable_Index --
948 --------------------
950 function Variable_Index (Name : String) return Natural is
951 begin
952 for J in 1 .. Num_Syms loop
953 if Matching_Strings (Symbols (J).all, Name) then
954 return J;
955 end if;
956 end loop;
958 return Natural'Last;
959 end Variable_Index;
961 -- Start of processing for GNATprep
963 begin
965 -- Parse the switches
967 loop
968 case GNAT.Command_Line.Getopt ("D: b c r s u") is
969 when ASCII.NUL =>
970 exit;
972 when 'D' =>
973 declare
974 S : String := GNAT.Command_Line.Parameter;
975 Index : Natural;
977 begin
978 Index := Ada.Strings.Fixed.Index (S, "=");
980 if Index = 0 then
981 Num_Syms := Num_Syms + 1;
982 Symbols (Num_Syms) := new String'(S);
983 Values (Num_Syms) := new String'("True");
985 else
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));
989 end if;
990 end;
992 when 'b' =>
993 Blank_Deleted_Lines := True;
995 when 'c' =>
996 Opt_Comment_Deleted_Lines := True;
998 when 'r' =>
999 Source_Ref_Pragma := True;
1001 when 's' =>
1002 List_Symbols := True;
1004 when 'u' =>
1005 Undefined_Is_False := True;
1007 when others =>
1008 raise Usage_Error;
1009 end case;
1010 end loop;
1012 -- Get the file names
1014 loop
1015 declare
1016 S : constant String := GNAT.Command_Line.Get_Argument;
1018 begin
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);
1027 else
1028 raise Usage_Error;
1029 end if;
1030 end;
1031 end loop;
1033 -- Test we had all the arguments needed
1035 if Infile.Name = null
1036 or else Outfile_Name = null
1037 then
1038 raise Usage_Error;
1039 end if;
1041 if Source_Ref_Pragma and (not Opt_Comment_Deleted_Lines) then
1042 Blank_Deleted_Lines := True;
1043 end if;
1045 -- Get symbol definitions
1047 if Deffile_Name /= null then
1048 Parse_Def_File;
1049 end if;
1051 if Num_Errors > 0 then
1052 raise Fatal_Error;
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);
1068 C1, C2 : Character;
1070 begin
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));
1075 if C1 < C2 then
1076 return True;
1078 elsif C1 > C2 then
1079 return False;
1080 end if;
1081 end loop;
1083 return L1 < L2;
1084 end Lt;
1086 procedure Move (From : Natural; To : Natural) is
1087 begin
1088 Symbols (To) := Symbols (From);
1089 Values (To) := Values (From);
1090 end Move;
1092 package Sort_Syms is new GNAT.Heap_Sort_G (Move, Lt);
1094 Max_L : Natural;
1095 -- Maximum length of any symbol
1097 -- Start of processing for List_Symbols_Case
1099 begin
1100 Sort_Syms.Sort (Num_Syms);
1102 Max_L := 7;
1103 for J in 1 .. Num_Syms loop
1104 Max_L := Natural'Max (Max_L, Symbols (J)'Length);
1105 end loop;
1107 New_Line;
1108 Put ("Symbol");
1110 for J in 1 .. Max_L - 5 loop
1111 Put (' ');
1112 end loop;
1114 Put_Line ("Value");
1116 Put ("------");
1118 for J in 1 .. Max_L - 5 loop
1119 Put (' ');
1120 end 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
1128 Put (' ');
1129 end loop;
1131 Put_Line (Values (J).all);
1132 end loop;
1134 New_Line;
1135 end List_Symbols_Case;
1136 end if;
1138 -- Open files and initialize preprocessing
1140 begin
1141 Open (Infile.File, In_File, Infile.Name.all);
1143 exception
1144 when Name_Error =>
1145 Put_Line (Standard_Error, "cannot open " & Infile.Name.all);
1146 raise Fatal_Error;
1147 end;
1149 begin
1150 Create (Outfile, Out_File, Outfile_Name.all);
1152 exception
1153 when Name_Error =>
1154 Put_Line (Standard_Error, "cannot create " & Outfile_Name.all);
1155 raise Fatal_Error;
1156 end;
1158 Infile.Line_Num := 0;
1159 Current_File_Name := Infile.Name;
1161 PP_Depth := 0;
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.
1167 <<Read_In_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
1172 -- including file.
1174 if Source_Ref_Pragma then
1175 Put_Line
1176 (Outfile, "pragma Source_Reference (" &
1177 Image (Infile.Line_Num + 1) &
1178 ", """ & Infile.Name.all & """);");
1179 end if;
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
1190 K := Scan_Keyword;
1192 case K is
1194 -- Include file
1196 when K_Include =>
1197 -- Ignore if Deleting is True
1199 if PP (PP_Depth).Deleting then
1200 goto Output;
1201 end if;
1203 Skip_Spaces;
1205 if Ptr >= Line_Length then
1206 Error ("no file to include");
1208 elsif Line_Buffer (Ptr) /= '"' then
1209 Error
1210 ("file to include must be specified as a literal string");
1212 else
1213 declare
1214 Start_File : constant Positive := Ptr + 1;
1216 begin
1217 Ptr := Line_Length;
1219 while Line_Buffer (Ptr) = ' '
1220 or else Line_Buffer (Ptr) = ASCII.HT
1221 loop
1222 Ptr := Ptr - 1;
1223 end loop;
1225 if Ptr <= Start_File
1226 or else Line_Buffer (Ptr) /= '"'
1227 then
1228 Error ("no string literal for included file");
1230 else
1231 if Infile.Next = null then
1232 Infile.Next := new Input;
1233 Infile.Next.Prev := Infile;
1234 end if;
1236 Infile := Infile.Next;
1237 Infile.Name :=
1238 new String'(Line_Buffer (Start_File .. Ptr - 1));
1240 -- Check for circularity: an file including itself,
1241 -- either directly or indirectly.
1243 declare
1244 File : Input_Ptr := Infile.Prev;
1246 begin
1247 while File /= null
1248 and then File.Name.all /= Infile.Name.all
1249 loop
1250 File := File.Prev;
1251 end loop;
1253 if File /= null then
1254 Infile := Infile.Prev;
1255 Error ("circularity in included files");
1257 while File.Prev /= null loop
1258 File := File.Prev;
1259 end loop;
1261 while File /= Infile.Next loop
1262 Error ('"' & File.Name.all &
1263 """ includes """ &
1264 File.Next.Name.all & '"');
1265 File := File.Next;
1266 end loop;
1268 else
1269 -- We have a file name and no circularity.
1270 -- Open the file and record an error if the
1271 -- file cannot be opened.
1273 begin
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.
1280 goto Read_In_File;
1282 exception
1283 when Name_Error =>
1285 -- We need to set the input file to
1286 -- the including file, so that the
1287 -- line number is correct when reporting
1288 -- the error.
1290 Infile := Infile.Prev;
1291 Error ("cannot open """ &
1292 Infile.Next.Name.all & '"');
1293 end;
1294 end if;
1295 end;
1296 end if;
1297 end;
1298 end if;
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.
1310 if K = K_If then
1311 PP_Depth := PP_Depth + 1;
1312 PP (PP_Depth) :=
1313 (If_Line => Infile.Line_Num,
1314 If_Name => Infile.Name,
1315 Else_Line => 0,
1316 Deleting => False,
1317 Match_Seen => PP (PP_Depth - 1).Deleting);
1319 elsif PP_Depth = 0 then
1320 Error ("no matching #if for this #elsif");
1321 goto Output;
1323 end if;
1325 PP (PP_Depth).Deleting := True;
1327 if not PP (PP_Depth).Match_Seen
1328 and then Eval_Condition = True
1329 then
1331 -- Case of match and no match yet in this #if
1333 PP (PP_Depth).Deleting := False;
1334 PP (PP_Depth).Match_Seen := True;
1335 No_Junk;
1336 end if;
1338 -- Processing for #else
1340 when K_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) &
1348 ")");
1350 else
1351 PP (PP_Depth).Else_Line := Infile.Line_Num;
1352 PP (PP_Depth).Deleting := PP (PP_Depth).Match_Seen;
1353 end if;
1355 No_Junk;
1357 -- Process for #end
1359 when K_End =>
1361 if PP_Depth = 0 then
1362 Error ("no matching #if for this #end");
1364 else
1365 Skip_Spaces;
1367 if Scan_Keyword /= K_If then
1368 Error ("expected if after #end");
1369 Ptr := Line_Length + 1;
1370 end if;
1372 Skip_Spaces;
1374 if Ptr > Line_Length
1375 or else Line_Buffer (Ptr) /= ';'
1376 then
1377 Error ("missing semicolon after #end if");
1378 else
1379 Ptr := Ptr + 1;
1380 end if;
1382 No_Junk;
1384 PP_Depth := PP_Depth - 1;
1385 end if;
1387 when others =>
1388 Error ("invalid preprocessor keyword syntax");
1390 end case;
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)
1402 else
1403 Ptr := 1;
1405 while Ptr < Line_Length loop
1406 exit when At_End_Of_Line;
1408 case Line_Buffer (Ptr) is
1410 when ''' =>
1412 -- Two special cases here:
1413 -- '"' => we don't want the " sign to appear as belonging
1414 -- to a string.
1415 -- '$' => this is obviously not a substitution, just skip it
1417 if Ptr < Line_Length - 1
1418 and then Line_Buffer (Ptr + 1) = '"'
1419 then
1420 Ptr := Ptr + 2;
1421 elsif Ptr < Line_Length - 2
1422 and then Line_Buffer (Ptr + 1 .. Ptr + 2) = "$'"
1423 then
1424 Ptr := Ptr + 2;
1425 end if;
1427 when '"' =>
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
1433 Ptr := Ptr + 1;
1434 while Ptr < Line_Length
1435 and then Line_Buffer (Ptr) /= '"'
1436 loop
1437 Ptr := Ptr + 1;
1438 end loop;
1440 when '$' =>
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
1451 if Matching_Strings
1452 (Symbols (J).all, Line_Buffer (Start_Sym .. End_Sym))
1453 then
1454 declare
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;
1461 begin
1462 Line_Buffer (NewP + 1 .. Line_Length + AdjL) :=
1463 Line_Buffer (End_Sym + 1 .. Line_Length);
1464 Line_Buffer (Ptr .. NewP) := Values (J).all;
1466 Ptr := NewP;
1467 Line_Length := Line_Length + AdjL;
1468 end;
1470 exit;
1471 end if;
1472 end loop;
1473 end if;
1475 when others =>
1476 null;
1478 end case;
1479 Ptr := Ptr + 1;
1480 end loop;
1481 end if;
1483 -- Here after dealing with preprocessor line, output current line
1485 <<Output>>
1487 if Is_Preprocessor_Line or else PP (PP_Depth).Deleting then
1488 if Blank_Deleted_Lines then
1489 New_Line (Outfile);
1491 elsif Opt_Comment_Deleted_Lines then
1492 if Line_Length = 0 then
1493 Put_Line (Outfile, "--!");
1494 else
1495 Put (Outfile, "--! ");
1496 Put_Line (Outfile, Line_Buffer (1 .. Line_Length));
1497 end if;
1498 end if;
1500 else
1501 Put_Line (Outfile, Line_Buffer (1 .. Line_Length));
1502 end if;
1503 end loop;
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;
1512 goto Read_In_File;
1513 end if;
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));
1519 else
1520 Error ("no matching #end for #if at line" &
1521 Natural'Image (PP (J).If_Line) &
1522 " of file """ & PP (J).If_Name.all & '"');
1523 end if;
1524 end loop;
1526 if Num_Errors = 0 then
1527 Close (Outfile);
1528 Set_Exit_Status (0);
1529 else
1530 Delete (Outfile);
1531 Set_Exit_Status (1);
1532 end if;
1534 exception
1535 when Usage_Error =>
1536 Help_Page;
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);
1542 Help_Page;
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);
1548 Help_Page;
1549 Set_Exit_Status (1);
1551 when Fatal_Error =>
1552 Set_Exit_Status (1);
1554 when Expression_Error =>
1555 Set_Exit_Status (1);
1557 end GNATprep;