2006-06-30 Andrew Pinski <pinskia@gmail.com>
[official-gcc.git] / gcc / ada / prep.adb
blobb2ec857b96d30fd319cc8c9b12000810a6e0a47b
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT COMPILER COMPONENTS --
4 -- --
5 -- P R E P --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2002-2005, 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, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, 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 Csets; use Csets;
28 with Err_Vars; use Err_Vars;
29 with Namet; use Namet;
30 with Opt; use Opt;
31 with Osint; use Osint;
32 with Output; use Output;
33 with Scans; use Scans;
34 with Snames; use Snames;
35 with Sinput;
36 with Stringt; use Stringt;
37 with Table;
39 with GNAT.Heap_Sort_G;
41 package body Prep is
43 use Symbol_Table;
45 type Token_Name_Array is array (Token_Type) of Name_Id;
46 Token_Names : constant Token_Name_Array :=
47 (Tok_Abort => Name_Abort,
48 Tok_Abs => Name_Abs,
49 Tok_Abstract => Name_Abstract,
50 Tok_Accept => Name_Accept,
51 Tok_Aliased => Name_Aliased,
52 Tok_All => Name_All,
53 Tok_Array => Name_Array,
54 Tok_And => Name_And,
55 Tok_At => Name_At,
56 Tok_Begin => Name_Begin,
57 Tok_Body => Name_Body,
58 Tok_Case => Name_Case,
59 Tok_Constant => Name_Constant,
60 Tok_Declare => Name_Declare,
61 Tok_Delay => Name_Delay,
62 Tok_Delta => Name_Delta,
63 Tok_Digits => Name_Digits,
64 Tok_Else => Name_Else,
65 Tok_Elsif => Name_Elsif,
66 Tok_End => Name_End,
67 Tok_Entry => Name_Entry,
68 Tok_Exception => Name_Exception,
69 Tok_Exit => Name_Exit,
70 Tok_For => Name_For,
71 Tok_Function => Name_Function,
72 Tok_Generic => Name_Generic,
73 Tok_Goto => Name_Goto,
74 Tok_If => Name_If,
75 Tok_Is => Name_Is,
76 Tok_Limited => Name_Limited,
77 Tok_Loop => Name_Loop,
78 Tok_Mod => Name_Mod,
79 Tok_New => Name_New,
80 Tok_Null => Name_Null,
81 Tok_Of => Name_Of,
82 Tok_Or => Name_Or,
83 Tok_Others => Name_Others,
84 Tok_Out => Name_Out,
85 Tok_Package => Name_Package,
86 Tok_Pragma => Name_Pragma,
87 Tok_Private => Name_Private,
88 Tok_Procedure => Name_Procedure,
89 Tok_Protected => Name_Protected,
90 Tok_Raise => Name_Raise,
91 Tok_Range => Name_Range,
92 Tok_Record => Name_Record,
93 Tok_Rem => Name_Rem,
94 Tok_Renames => Name_Renames,
95 Tok_Requeue => Name_Requeue,
96 Tok_Return => Name_Return,
97 Tok_Reverse => Name_Reverse,
98 Tok_Select => Name_Select,
99 Tok_Separate => Name_Separate,
100 Tok_Subtype => Name_Subtype,
101 Tok_Tagged => Name_Tagged,
102 Tok_Task => Name_Task,
103 Tok_Terminate => Name_Terminate,
104 Tok_Then => Name_Then,
105 Tok_Type => Name_Type,
106 Tok_Until => Name_Until,
107 Tok_Use => Name_Use,
108 Tok_When => Name_When,
109 Tok_While => Name_While,
110 Tok_With => Name_With,
111 Tok_Xor => Name_Xor,
112 others => No_Name);
114 Already_Initialized : Boolean := False;
115 -- Used to avoid repetition of the part of the initialisation that needs
116 -- to be done only once.
118 Empty_String : String_Id;
119 -- "", as a string_id
121 String_False : String_Id;
122 -- "false", as a string_id
124 Name_Defined : Name_Id;
125 -- defined, as a name_id
127 ---------------
128 -- Behaviour --
129 ---------------
131 -- Accesses to procedure specified by procedure Initialize
133 Error_Msg : Error_Msg_Proc;
134 -- Report an error
136 Scan : Scan_Proc;
137 -- Scan one token
139 Set_Ignore_Errors : Set_Ignore_Errors_Proc;
140 -- Indicate if error should be taken into account
142 Put_Char : Put_Char_Proc;
143 -- Output one character
145 New_EOL : New_EOL_Proc;
146 -- Output an end of line indication
148 -------------------------------
149 -- State of the Preprocessor --
150 -------------------------------
152 type Pp_State is record
153 If_Ptr : Source_Ptr;
154 -- The location of the #if statement.
155 -- Used to flag #if with no corresponding #end if, at the end.
157 Else_Ptr : Source_Ptr;
158 -- The location of the #else statement.
159 -- Used to detect multiple #else.
161 Deleting : Boolean;
162 -- Set to True when the code should be deleted or commented out
164 Match_Seen : Boolean;
165 -- Set to True when a condition in an #if or an #elsif is True.
166 -- Also set to True if Deleting at the previous level is True.
167 -- Used to decide if Deleting should be set to True in a following
168 -- #elsif or #else.
170 end record;
172 type Pp_Depth is new Nat;
174 Ground : constant Pp_Depth := 0;
176 package Pp_States is new Table.Table
177 (Table_Component_Type => Pp_State,
178 Table_Index_Type => Pp_Depth,
179 Table_Low_Bound => 1,
180 Table_Initial => 10,
181 Table_Increment => 10,
182 Table_Name => "Prep.Pp_States");
183 -- A stack of the states of the preprocessor, for nested #if
185 type Operator is (None, Op_Or, Op_And);
187 -----------------
188 -- Subprograms --
189 -----------------
191 function Deleting return Boolean;
192 -- Return True if code should be deleted or commented out
194 function Expression (Evaluate_It : Boolean) return Boolean;
195 -- Evaluate a condition in an #if or an #elsif statement.
196 -- If Evaluate_It is False, the condition is effectively evaluated,
197 -- otherwise, only the syntax is checked.
199 procedure Go_To_End_Of_Line;
200 -- Advance the scan pointer until we reach an end of line or the end
201 -- of the buffer.
203 function Matching_Strings (S1, S2 : String_Id) return Boolean;
204 -- Returns True if the two string parameters are equal (case insensitive)
206 ---------------------------------------
207 -- Change_Reserved_Keyword_To_Symbol --
208 ---------------------------------------
210 procedure Change_Reserved_Keyword_To_Symbol
211 (All_Keywords : Boolean := False)
213 New_Name : constant Name_Id := Token_Names (Token);
215 begin
216 if New_Name /= No_Name then
217 case Token is
218 when Tok_If | Tok_Else | Tok_Elsif | Tok_End |
219 Tok_And | Tok_Or | Tok_Then =>
220 if All_Keywords then
221 Token := Tok_Identifier;
222 Token_Name := New_Name;
223 end if;
225 when others =>
226 Token := Tok_Identifier;
227 Token_Name := New_Name;
228 end case;
229 end if;
230 end Change_Reserved_Keyword_To_Symbol;
232 ------------------------------------------
233 -- Check_Command_Line_Symbol_Definition --
234 ------------------------------------------
236 procedure Check_Command_Line_Symbol_Definition
237 (Definition : String;
238 Data : out Symbol_Data)
240 Index : Natural := 0;
241 Result : Symbol_Data;
243 begin
244 -- Look for the character '='
246 for J in Definition'Range loop
247 if Definition (J) = '=' then
248 Index := J;
249 exit;
250 end if;
251 end loop;
253 -- If no character '=', then the value is True
255 if Index = 0 then
256 -- Put the symbol in the name buffer
258 Name_Len := Definition'Length;
259 Name_Buffer (1 .. Name_Len) := Definition;
260 Result := True_Value;
262 elsif Index = Definition'First then
263 Fail ("invalid symbol definition """, Definition, """");
265 else
266 -- Put the symbol in the name buffer
268 Name_Len := Index - Definition'First;
269 Name_Buffer (1 .. Name_Len) :=
270 String'(Definition (Definition'First .. Index - 1));
272 -- Check the syntax of the value
274 if Definition (Index + 1) /= '"'
275 or else Definition (Definition'Last) /= '"'
276 then
277 for J in Index + 1 .. Definition'Last loop
278 case Definition (J) is
279 when '_' | '.' | '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' =>
280 null;
282 when others =>
283 Fail ("illegal value """,
284 Definition (Index + 1 .. Definition'Last),
285 """");
286 end case;
287 end loop;
288 end if;
290 -- And put the value in the result
292 Result.Is_A_String := False;
293 Start_String;
294 Store_String_Chars (Definition (Index + 1 .. Definition'Last));
295 Result.Value := End_String;
296 end if;
298 -- Now, check the syntax of the symbol (we don't allow accented and
299 -- wide characters)
301 if Name_Buffer (1) not in 'a' .. 'z'
302 and then Name_Buffer (1) not in 'A' .. 'Z'
303 then
304 Fail ("symbol """,
305 Name_Buffer (1 .. Name_Len),
306 """ does not start with a letter");
307 end if;
309 for J in 2 .. Name_Len loop
310 case Name_Buffer (J) is
311 when 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' =>
312 null;
314 when '_' =>
315 if J = Name_Len then
316 Fail ("symbol """,
317 Name_Buffer (1 .. Name_Len),
318 """ end with a '_'");
320 elsif Name_Buffer (J + 1) = '_' then
321 Fail ("symbol """,
322 Name_Buffer (1 .. Name_Len),
323 """ contains consecutive '_'");
324 end if;
326 when others =>
327 Fail ("symbol """,
328 Name_Buffer (1 .. Name_Len),
329 """ contains illegal character(s)");
330 end case;
331 end loop;
333 Result.On_The_Command_Line := True;
335 -- Put the symbol name in the result
337 declare
338 Sym : constant String := Name_Buffer (1 .. Name_Len);
340 begin
341 for Index in 1 .. Name_Len loop
342 Name_Buffer (Index) := Fold_Lower (Name_Buffer (Index));
343 end loop;
345 Result.Symbol := Name_Find;
346 Name_Len := Sym'Length;
347 Name_Buffer (1 .. Name_Len) := Sym;
348 Result.Original := Name_Find;
349 end;
351 Data := Result;
352 end Check_Command_Line_Symbol_Definition;
354 --------------
355 -- Deleting --
356 --------------
358 function Deleting return Boolean is
359 begin
360 -- Always return False when not inside an #if statement
362 if Pp_States.Last = Ground then
363 return False;
365 else
366 return Pp_States.Table (Pp_States.Last).Deleting;
367 end if;
368 end Deleting;
370 ----------------
371 -- Expression --
372 ----------------
374 function Expression (Evaluate_It : Boolean) return Boolean is
375 Evaluation : Boolean := Evaluate_It;
376 -- Is set to False after an "or else" when left term is True and
377 -- after an "and then" when left term is False.
379 Final_Result : Boolean := False;
381 Current_Result : Boolean := False;
382 -- Value of a term
384 Current_Operator : Operator := None;
385 Symbol1 : Symbol_Id;
386 Symbol2 : Symbol_Id;
387 Symbol_Name1 : Name_Id;
388 Symbol_Name2 : Name_Id;
389 Symbol_Pos1 : Source_Ptr;
390 Symbol_Pos2 : Source_Ptr;
391 Symbol_Value1 : String_Id;
392 Symbol_Value2 : String_Id;
394 begin
395 -- Loop for each term
397 loop
398 Change_Reserved_Keyword_To_Symbol;
400 Current_Result := False;
402 case Token is
404 when Tok_Left_Paren =>
406 -- ( expression )
408 Scan.all;
409 Current_Result := Expression (Evaluation);
411 if Token = Tok_Right_Paren then
412 Scan.all;
414 else
415 Error_Msg ("`)` expected", Token_Ptr);
416 end if;
418 when Tok_Not =>
420 -- not expression
422 Scan.all;
423 Current_Result := not Expression (Evaluation);
425 when Tok_Identifier =>
426 Symbol_Name1 := Token_Name;
427 Symbol_Pos1 := Token_Ptr;
428 Scan.all;
430 if Token = Tok_Apostrophe then
432 -- symbol'Defined
434 Scan.all;
436 if Token = Tok_Identifier
437 and then Token_Name = Name_Defined
438 then
439 Scan.all;
441 else
442 Error_Msg ("identifier `Defined` expected", Token_Ptr);
443 end if;
445 if Evaluation then
446 Current_Result := Index_Of (Symbol_Name1) /= No_Symbol;
447 end if;
449 elsif Token = Tok_Equal then
450 Scan.all;
452 Change_Reserved_Keyword_To_Symbol;
454 if Token = Tok_Identifier then
456 -- symbol = symbol
458 Symbol_Name2 := Token_Name;
459 Symbol_Pos2 := Token_Ptr;
460 Scan.all;
462 if Evaluation then
463 Symbol1 := Index_Of (Symbol_Name1);
465 if Symbol1 = No_Symbol then
466 if Undefined_Symbols_Are_False then
467 Symbol_Value1 := String_False;
469 else
470 Error_Msg_Name_1 := Symbol_Name1;
471 Error_Msg ("unknown symbol %", Symbol_Pos1);
472 Symbol_Value1 := No_String;
473 end if;
475 else
476 Symbol_Value1 :=
477 Mapping.Table (Symbol1).Value;
478 end if;
480 Symbol2 := Index_Of (Symbol_Name2);
482 if Symbol2 = No_Symbol then
483 if Undefined_Symbols_Are_False then
484 Symbol_Value2 := String_False;
486 else
487 Error_Msg_Name_1 := Symbol_Name2;
488 Error_Msg ("unknown symbol %", Symbol_Pos2);
489 Symbol_Value2 := No_String;
490 end if;
492 else
493 Symbol_Value2 := Mapping.Table (Symbol2).Value;
494 end if;
496 if Symbol_Value1 /= No_String
497 and then Symbol_Value2 /= No_String
498 then
499 Current_Result := Matching_Strings
500 (Symbol_Value1, Symbol_Value2);
501 end if;
502 end if;
504 elsif Token = Tok_String_Literal then
506 -- symbol = "value"
508 if Evaluation then
509 Symbol1 := Index_Of (Symbol_Name1);
511 if Symbol1 = No_Symbol then
512 if Undefined_Symbols_Are_False then
513 Symbol_Value1 := String_False;
515 else
516 Error_Msg_Name_1 := Symbol_Name1;
517 Error_Msg ("unknown symbol %", Symbol_Pos1);
518 Symbol_Value1 := No_String;
519 end if;
521 else
522 Symbol_Value1 := Mapping.Table (Symbol1).Value;
523 end if;
525 if Symbol_Value1 /= No_String then
526 Current_Result :=
527 Matching_Strings
528 (Symbol_Value1,
529 String_Literal_Id);
530 end if;
531 end if;
533 Scan.all;
535 else
536 Error_Msg
537 ("symbol or literal string expected", Token_Ptr);
538 end if;
540 else
541 -- symbol (True or False)
543 if Evaluation then
544 Symbol1 := Index_Of (Symbol_Name1);
546 if Symbol1 = No_Symbol then
547 if Undefined_Symbols_Are_False then
548 Symbol_Value1 := String_False;
550 else
551 Error_Msg_Name_1 := Symbol_Name1;
552 Error_Msg ("unknown symbol %", Symbol_Pos1);
553 Symbol_Value1 := No_String;
554 end if;
556 else
557 Symbol_Value1 := Mapping.Table (Symbol1).Value;
558 end if;
560 if Symbol_Value1 /= No_String then
561 String_To_Name_Buffer (Symbol_Value1);
563 for Index in 1 .. Name_Len loop
564 Name_Buffer (Index) :=
565 Fold_Lower (Name_Buffer (Index));
566 end loop;
568 if Name_Buffer (1 .. Name_Len) = "true" then
569 Current_Result := True;
571 elsif Name_Buffer (1 .. Name_Len) = "false" then
572 Current_Result := False;
574 else
575 Error_Msg_Name_1 := Symbol_Name1;
576 Error_Msg
577 ("value of symbol % is not True or False",
578 Symbol_Pos1);
579 end if;
580 end if;
581 end if;
582 end if;
584 when others =>
585 Error_Msg ("`(`, NOT or symbol expected", Token_Ptr);
586 end case;
588 -- Update the cumulative final result
590 case Current_Operator is
591 when None =>
592 Final_Result := Current_Result;
594 when Op_Or =>
595 Final_Result := Final_Result or Current_Result;
597 when Op_And =>
598 Final_Result := Final_Result and Current_Result;
599 end case;
601 -- Check the next operator
603 if Token = Tok_And then
604 if Current_Operator = Op_Or then
605 Error_Msg ("mixing OR and AND is not allowed", Token_Ptr);
606 end if;
608 Current_Operator := Op_And;
609 Scan.all;
611 if Token = Tok_Then then
612 Scan.all;
614 if Final_Result = False then
615 Evaluation := False;
616 end if;
617 end if;
619 elsif Token = Tok_Or then
620 if Current_Operator = Op_And then
621 Error_Msg ("mixing AND and OR is not allowed", Token_Ptr);
622 end if;
624 Current_Operator := Op_Or;
625 Scan.all;
627 if Token = Tok_Else then
628 Scan.all;
630 if Final_Result then
631 Evaluation := False;
632 end if;
633 end if;
635 else
636 -- No operator: exit the term loop
638 exit;
639 end if;
640 end loop;
642 return Final_Result;
643 end Expression;
645 -----------------------
646 -- Go_To_End_Of_Line --
647 -----------------------
649 procedure Go_To_End_Of_Line is
650 begin
651 -- Scan until we get an end of line or we reach the end of the buffer
653 while Token /= Tok_End_Of_Line
654 and then Token /= Tok_EOF
655 loop
656 Scan.all;
657 end loop;
658 end Go_To_End_Of_Line;
660 --------------
661 -- Index_Of --
662 --------------
664 function Index_Of (Symbol : Name_Id) return Symbol_Id is
665 begin
666 if Mapping.Table /= null then
667 for J in Symbol_Id range 1 .. Symbol_Table.Last (Mapping) loop
668 if Mapping.Table (J).Symbol = Symbol then
669 return J;
670 end if;
671 end loop;
672 end if;
674 return No_Symbol;
675 end Index_Of;
677 ----------------
678 -- Preprocess --
679 ----------------
681 procedure Preprocess is
682 Start_Of_Processing : Source_Ptr;
683 Cond : Boolean;
684 Preprocessor_Line : Boolean := False;
686 procedure Output (From, To : Source_Ptr);
687 -- Output the characters with indices From .. To in the buffer
688 -- to the output file.
690 procedure Output_Line (From, To : Source_Ptr);
691 -- Output a line or the end of a line from the buffer to the output
692 -- file, followed by an end of line terminator. Depending on the value
693 -- of Deleting and the switches, the line may be commented out, blank or
694 -- not output at all.
696 ------------
697 -- Output --
698 ------------
700 procedure Output (From, To : Source_Ptr) is
701 begin
702 for J in From .. To loop
703 Put_Char (Sinput.Source (J));
704 end loop;
705 end Output;
707 -----------------
708 -- Output_Line --
709 -----------------
711 procedure Output_Line (From, To : Source_Ptr) is
712 begin
713 if Deleting or Preprocessor_Line then
714 if Blank_Deleted_Lines then
715 New_EOL.all;
717 elsif Comment_Deleted_Lines then
718 Put_Char ('-');
719 Put_Char ('-');
720 Put_Char ('!');
722 if From < To then
723 Put_Char (' ');
724 Output (From, To);
725 end if;
727 New_EOL.all;
728 end if;
730 else
731 Output (From, To);
732 New_EOL.all;
733 end if;
734 end Output_Line;
736 -- Start of processing for Preprocess
738 begin
739 Start_Of_Processing := Scan_Ptr;
741 -- We need to call Scan for the first time, because Initialize_Scanner
742 -- is no longer doing it.
744 Scan.all;
746 Input_Line_Loop : loop
747 exit Input_Line_Loop when Token = Tok_EOF;
749 Preprocessor_Line := False;
751 if Token /= Tok_End_Of_Line then
753 -- Preprocessor line
755 if Token = Tok_Special and then Special_Character = '#' then
756 Preprocessor_Line := True;
757 Scan.all;
759 case Token is
761 -- #if
763 when Tok_If =>
764 declare
765 If_Ptr : constant Source_Ptr := Token_Ptr;
767 begin
768 Scan.all;
769 Cond := Expression (not Deleting);
771 -- Check for an eventual "then"
773 if Token = Tok_Then then
774 Scan.all;
775 end if;
777 -- It is an error to have trailing characters after
778 -- the condition or "then".
780 if Token /= Tok_End_Of_Line
781 and then Token /= Tok_EOF
782 then
783 Error_Msg
784 ("extraneous text on preprocessor line",
785 Token_Ptr);
786 Go_To_End_Of_Line;
787 end if;
789 declare
790 -- Set the initial state of this new "#if".
791 -- This must be done before incrementing the
792 -- Last of the table, otherwise function
793 -- Deleting does not report the correct value.
795 New_State : constant Pp_State :=
796 (If_Ptr => If_Ptr,
797 Else_Ptr => 0,
798 Deleting => Deleting or (not Cond),
799 Match_Seen => Deleting or Cond);
801 begin
802 Pp_States.Increment_Last;
803 Pp_States.Table (Pp_States.Last) := New_State;
804 end;
805 end;
807 -- #elsif
809 when Tok_Elsif =>
810 Cond := False;
812 if Pp_States.Last = 0
813 or else Pp_States.Table (Pp_States.Last).Else_Ptr
814 /= 0
815 then
816 Error_Msg ("no IF for this ELSIF", Token_Ptr);
818 else
819 Cond :=
820 not Pp_States.Table (Pp_States.Last).Match_Seen;
821 end if;
823 Scan.all;
824 Cond := Expression (Cond);
826 -- Check for an eventual "then"
828 if Token = Tok_Then then
829 Scan.all;
830 end if;
832 -- It is an error to have trailing characters after
833 -- the condition or "then".
835 if Token /= Tok_End_Of_Line
836 and then Token /= Tok_EOF
837 then
838 Error_Msg
839 ("extraneous text on preprocessor line",
840 Token_Ptr);
842 Go_To_End_Of_Line;
843 end if;
845 -- Depending on the value of the condition, set the
846 -- new values of Deleting and Match_Seen.
847 if Pp_States.Last > 0 then
848 if Pp_States.Table (Pp_States.Last).Match_Seen then
849 Pp_States.Table (Pp_States.Last).Deleting :=
850 True;
851 else
852 if Cond then
853 Pp_States.Table (Pp_States.Last).Match_Seen :=
854 True;
855 Pp_States.Table (Pp_States.Last).Deleting :=
856 False;
857 end if;
858 end if;
859 end if;
861 -- #else
863 when Tok_Else =>
864 if Pp_States.Last = 0 then
865 Error_Msg ("no IF for this ELSE", Token_Ptr);
867 elsif
868 Pp_States.Table (Pp_States.Last).Else_Ptr /= 0
869 then
870 Error_Msg ("duplicate ELSE line", Token_Ptr);
871 end if;
873 -- Set the possibly new values of Deleting and
874 -- Match_Seen.
876 if Pp_States.Last > 0 then
877 if Pp_States.Table (Pp_States.Last).Match_Seen then
878 Pp_States.Table (Pp_States.Last).Deleting :=
879 True;
881 else
882 Pp_States.Table (Pp_States.Last).Match_Seen :=
883 True;
884 Pp_States.Table (Pp_States.Last).Deleting :=
885 False;
886 end if;
888 -- Set the Else_Ptr to check for illegal #elsif
889 -- later.
891 Pp_States.Table (Pp_States.Last).Else_Ptr :=
892 Token_Ptr;
893 end if;
895 Scan.all;
897 -- It is an error to have characters after "#else"
898 if Token /= Tok_End_Of_Line
899 and then Token /= Tok_EOF
900 then
901 Error_Msg
902 ("extraneous text on preprocessor line",
903 Token_Ptr);
904 Go_To_End_Of_Line;
905 end if;
907 -- #end if;
909 when Tok_End =>
910 if Pp_States.Last = 0 then
911 Error_Msg ("no IF for this END", Token_Ptr);
912 end if;
914 Scan.all;
916 if Token /= Tok_If then
917 Error_Msg ("IF expected", Token_Ptr);
919 else
920 Scan.all;
922 if Token /= Tok_Semicolon then
923 Error_Msg ("`;` Expected", Token_Ptr);
925 else
926 Scan.all;
928 -- It is an error to have character after
929 -- "#end if;".
930 if Token /= Tok_End_Of_Line
931 and then Token /= Tok_EOF
932 then
933 Error_Msg
934 ("extraneous text on preprocessor line",
935 Token_Ptr);
936 end if;
937 end if;
938 end if;
940 -- In case of one of the errors above, skip the tokens
941 -- until the end of line is reached.
943 Go_To_End_Of_Line;
945 -- Decrement the depth of the #if stack
947 if Pp_States.Last > 0 then
948 Pp_States.Decrement_Last;
949 end if;
951 -- Illegal preprocessor line
953 when others =>
954 if Pp_States.Last = 0 then
955 Error_Msg ("IF expected", Token_Ptr);
957 elsif
958 Pp_States.Table (Pp_States.Last).Else_Ptr = 0
959 then
960 Error_Msg ("IF, ELSIF, ELSE, or `END IF` expected",
961 Token_Ptr);
963 else
964 Error_Msg ("IF or `END IF` expected", Token_Ptr);
965 end if;
967 -- Skip to the end of this illegal line
969 Go_To_End_Of_Line;
970 end case;
972 -- Not a preprocessor line
974 else
975 -- Do not report errors for those lines, even if there are
976 -- Ada parsing errors.
978 Set_Ignore_Errors (To => True);
980 if Deleting then
981 Go_To_End_Of_Line;
983 else
984 while Token /= Tok_End_Of_Line
985 and then Token /= Tok_EOF
986 loop
987 if Token = Tok_Special
988 and then Special_Character = '$'
989 then
990 declare
991 Dollar_Ptr : constant Source_Ptr := Token_Ptr;
992 Symbol : Symbol_Id;
994 begin
995 Scan.all;
996 Change_Reserved_Keyword_To_Symbol;
998 if Token = Tok_Identifier
999 and then Token_Ptr = Dollar_Ptr + 1
1000 then
1001 -- $symbol
1003 Symbol := Index_Of (Token_Name);
1005 -- If symbol exists, replace by its value
1007 if Symbol /= No_Symbol then
1008 Output (Start_Of_Processing, Dollar_Ptr - 1);
1009 Start_Of_Processing := Scan_Ptr;
1010 String_To_Name_Buffer
1011 (Mapping.Table (Symbol).Value);
1013 if Mapping.Table (Symbol).Is_A_String then
1015 -- Value is an Ada string
1017 Put_Char ('"');
1019 for J in 1 .. Name_Len loop
1020 Put_Char (Name_Buffer (J));
1022 if Name_Buffer (J) = '"' then
1023 Put_Char ('"');
1024 end if;
1025 end loop;
1027 Put_Char ('"');
1029 else
1030 -- Value is a sequence of characters, not
1031 -- an Ada string.
1033 for J in 1 .. Name_Len loop
1034 Put_Char (Name_Buffer (J));
1035 end loop;
1036 end if;
1037 end if;
1038 end if;
1039 end;
1040 end if;
1042 Scan.all;
1043 end loop;
1044 end if;
1046 Set_Ignore_Errors (To => False);
1047 end if;
1048 end if;
1050 pragma Assert (Token = Tok_End_Of_Line or Token = Tok_EOF);
1052 -- At this point, the token is either end of line or EOF.
1053 -- The line to possibly output stops just before the token.
1055 Output_Line (Start_Of_Processing, Token_Ptr - 1);
1057 -- If we are at the end of a line, the scan pointer is at the first
1058 -- non blank character, not necessarily the first character of the
1059 -- line; so, we have to deduct Start_Of_Processing from the token
1060 -- pointer.
1062 if Token = Tok_End_Of_Line then
1063 if (Sinput.Source (Token_Ptr) = ASCII.CR
1064 and then Sinput.Source (Token_Ptr + 1) = ASCII.LF)
1065 or else
1066 (Sinput.Source (Token_Ptr) = ASCII.CR
1067 and then Sinput.Source (Token_Ptr + 1) = ASCII.LF)
1068 then
1069 Start_Of_Processing := Token_Ptr + 2;
1070 else
1071 Start_Of_Processing := Token_Ptr + 1;
1072 end if;
1073 end if;
1075 -- Now, scan the first token of the next line. If the token is EOF,
1076 -- the scan ponter will not move, and the token will still be EOF.
1078 Set_Ignore_Errors (To => True);
1079 Scan.all;
1080 Set_Ignore_Errors (To => False);
1081 end loop Input_Line_Loop;
1083 -- Report an error for any missing some "#end if;"
1085 for Level in reverse 1 .. Pp_States.Last loop
1086 Error_Msg ("no `END IF` for this IF", Pp_States.Table (Level).If_Ptr);
1087 end loop;
1088 end Preprocess;
1090 ----------------
1091 -- Initialize --
1092 ----------------
1094 procedure Initialize
1095 (Error_Msg : Error_Msg_Proc;
1096 Scan : Scan_Proc;
1097 Set_Ignore_Errors : Set_Ignore_Errors_Proc;
1098 Put_Char : Put_Char_Proc;
1099 New_EOL : New_EOL_Proc)
1101 begin
1102 if not Already_Initialized then
1103 Start_String;
1104 Store_String_Chars ("True");
1105 True_Value.Value := End_String;
1107 Start_String;
1108 Empty_String := End_String;
1110 Name_Len := 7;
1111 Name_Buffer (1 .. Name_Len) := "defined";
1112 Name_Defined := Name_Find;
1114 Start_String;
1115 Store_String_Chars ("False");
1116 String_False := End_String;
1118 Already_Initialized := True;
1119 end if;
1121 Prep.Error_Msg := Error_Msg;
1122 Prep.Scan := Scan;
1123 Prep.Set_Ignore_Errors := Set_Ignore_Errors;
1124 Prep.Put_Char := Put_Char;
1125 Prep.New_EOL := New_EOL;
1126 end Initialize;
1128 ------------------
1129 -- List_Symbols --
1130 ------------------
1132 procedure List_Symbols (Foreword : String) is
1133 Order : array (0 .. Integer (Symbol_Table.Last (Mapping)))
1134 of Symbol_Id;
1135 -- After alphabetical sorting, this array stores thehe indices of
1136 -- the symbols in the order they are displayed.
1138 function Lt (Op1, Op2 : Natural) return Boolean;
1139 -- Comparison routine for sort call
1141 procedure Move (From : Natural; To : Natural);
1142 -- Move routine for sort call
1144 --------
1145 -- Lt --
1146 --------
1148 function Lt (Op1, Op2 : Natural) return Boolean is
1149 S1 : constant String :=
1150 Get_Name_String (Mapping.Table (Order (Op1)).Symbol);
1151 S2 : constant String :=
1152 Get_Name_String (Mapping.Table (Order (Op2)).Symbol);
1154 begin
1155 return S1 < S2;
1156 end Lt;
1158 ----------
1159 -- Move --
1160 ----------
1162 procedure Move (From : Natural; To : Natural) is
1163 begin
1164 Order (To) := Order (From);
1165 end Move;
1167 package Sort_Syms is new GNAT.Heap_Sort_G (Move, Lt);
1169 Max_L : Natural;
1170 -- Maximum length of any symbol
1172 -- Start of processing for List_Symbols_Case
1174 begin
1175 if Symbol_Table.Last (Mapping) = 0 then
1176 return;
1177 end if;
1179 if Foreword'Length > 0 then
1180 Write_Eol;
1181 Write_Line (Foreword);
1183 for J in Foreword'Range loop
1184 Write_Char ('=');
1185 end loop;
1186 end if;
1188 -- Initialize the order
1190 for J in Order'Range loop
1191 Order (J) := Symbol_Id (J);
1192 end loop;
1194 -- Sort alphabetically
1196 Sort_Syms.Sort (Order'Last);
1198 Max_L := 7;
1200 for J in 1 .. Symbol_Table.Last (Mapping) loop
1201 Get_Name_String (Mapping.Table (J).Original);
1202 Max_L := Integer'Max (Max_L, Name_Len);
1203 end loop;
1205 Write_Eol;
1206 Write_Str ("Symbol");
1208 for J in 1 .. Max_L - 5 loop
1209 Write_Char (' ');
1210 end loop;
1212 Write_Line ("Value");
1214 Write_Str ("------");
1216 for J in 1 .. Max_L - 5 loop
1217 Write_Char (' ');
1218 end loop;
1220 Write_Line ("------");
1222 for J in 1 .. Order'Last loop
1223 declare
1224 Data : constant Symbol_Data := Mapping.Table (Order (J));
1226 begin
1227 Get_Name_String (Data.Original);
1228 Write_Str (Name_Buffer (1 .. Name_Len));
1230 for K in Name_Len .. Max_L loop
1231 Write_Char (' ');
1232 end loop;
1234 String_To_Name_Buffer (Data.Value);
1236 if Data.Is_A_String then
1237 Write_Char ('"');
1239 for J in 1 .. Name_Len loop
1240 Write_Char (Name_Buffer (J));
1242 if Name_Buffer (J) = '"' then
1243 Write_Char ('"');
1244 end if;
1245 end loop;
1247 Write_Char ('"');
1249 else
1250 Write_Str (Name_Buffer (1 .. Name_Len));
1251 end if;
1252 end;
1254 Write_Eol;
1255 end loop;
1257 Write_Eol;
1258 end List_Symbols;
1260 ----------------------
1261 -- Matching_Strings --
1262 ----------------------
1264 function Matching_Strings (S1, S2 : String_Id) return Boolean is
1265 begin
1266 String_To_Name_Buffer (S1);
1268 for Index in 1 .. Name_Len loop
1269 Name_Buffer (Index) := Fold_Lower (Name_Buffer (Index));
1270 end loop;
1272 declare
1273 String1 : constant String := Name_Buffer (1 .. Name_Len);
1275 begin
1276 String_To_Name_Buffer (S2);
1278 for Index in 1 .. Name_Len loop
1279 Name_Buffer (Index) := Fold_Lower (Name_Buffer (Index));
1280 end loop;
1282 return String1 = Name_Buffer (1 .. Name_Len);
1283 end;
1284 end Matching_Strings;
1286 --------------------
1287 -- Parse_Def_File --
1288 --------------------
1290 procedure Parse_Def_File is
1291 Symbol : Symbol_Id;
1292 Symbol_Name : Name_Id;
1293 Original_Name : Name_Id;
1294 Data : Symbol_Data;
1295 Value_Start : Source_Ptr;
1296 Value_End : Source_Ptr;
1297 Ch : Character;
1299 use ASCII;
1301 begin
1302 Def_Line_Loop :
1303 loop
1304 Scan.all;
1306 exit Def_Line_Loop when Token = Tok_EOF;
1308 if Token /= Tok_End_Of_Line then
1309 Change_Reserved_Keyword_To_Symbol;
1311 if Token /= Tok_Identifier then
1312 Error_Msg ("identifier expected", Token_Ptr);
1313 goto Cleanup;
1314 end if;
1316 Symbol_Name := Token_Name;
1317 Name_Len := 0;
1319 for Ptr in Token_Ptr .. Scan_Ptr - 1 loop
1320 Name_Len := Name_Len + 1;
1321 Name_Buffer (Name_Len) := Sinput.Source (Ptr);
1322 end loop;
1324 Original_Name := Name_Find;
1325 Scan.all;
1327 if Token /= Tok_Colon_Equal then
1328 Error_Msg ("`:=` expected", Token_Ptr);
1329 goto Cleanup;
1330 end if;
1332 Scan.all;
1334 if Token = Tok_String_Literal then
1335 Data := (Symbol => Symbol_Name,
1336 Original => Original_Name,
1337 On_The_Command_Line => False,
1338 Is_A_String => True,
1339 Value => String_Literal_Id);
1341 Scan.all;
1343 if Token /= Tok_End_Of_Line and then Token /= Tok_EOF then
1344 Error_Msg ("extraneous text in definition", Token_Ptr);
1345 goto Cleanup;
1346 end if;
1348 elsif Token = Tok_End_Of_Line or Token = Tok_EOF then
1349 Data := (Symbol => Symbol_Name,
1350 Original => Original_Name,
1351 On_The_Command_Line => False,
1352 Is_A_String => False,
1353 Value => Empty_String);
1355 else
1356 Value_Start := Token_Ptr;
1357 Value_End := Token_Ptr - 1;
1358 Scan_Ptr := Token_Ptr;
1360 Value_Chars_Loop :
1361 loop
1362 Ch := Sinput.Source (Scan_Ptr);
1364 case Ch is
1365 when '_' | '.' | '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' =>
1366 Value_End := Scan_Ptr;
1367 Scan_Ptr := Scan_Ptr + 1;
1369 when ' ' | HT | VT | CR | LF | FF =>
1370 exit Value_Chars_Loop;
1372 when others =>
1373 Error_Msg ("illegal character", Scan_Ptr);
1374 goto Cleanup;
1375 end case;
1376 end loop Value_Chars_Loop;
1378 Scan.all;
1380 if Token /= Tok_End_Of_Line and then Token /= Tok_EOF then
1381 Error_Msg ("extraneous text in definition", Token_Ptr);
1382 goto Cleanup;
1383 end if;
1385 Start_String;
1387 while Value_Start <= Value_End loop
1388 Store_String_Char (Sinput.Source (Value_Start));
1389 Value_Start := Value_Start + 1;
1390 end loop;
1392 Data := (Symbol => Symbol_Name,
1393 Original => Original_Name,
1394 On_The_Command_Line => False,
1395 Is_A_String => False,
1396 Value => End_String);
1397 end if;
1399 -- Now that we have the value, get the symbol index
1401 Symbol := Index_Of (Symbol_Name);
1403 if Symbol /= No_Symbol then
1404 -- If we already have an entry for this symbol, replace it
1405 -- with the new value, except if the symbol was declared
1406 -- on the command line.
1408 if Mapping.Table (Symbol).On_The_Command_Line then
1409 goto Continue;
1410 end if;
1412 else
1413 -- As it is the first time we see this symbol, create a new
1414 -- entry in the table.
1416 if Mapping.Table = null then
1417 Symbol_Table.Init (Mapping);
1418 end if;
1420 Symbol_Table.Increment_Last (Mapping);
1421 Symbol := Symbol_Table.Last (Mapping);
1422 end if;
1424 Mapping.Table (Symbol) := Data;
1425 goto Continue;
1427 <<Cleanup>>
1428 Set_Ignore_Errors (To => True);
1430 while Token /= Tok_End_Of_Line and Token /= Tok_EOF loop
1431 Scan.all;
1432 end loop;
1434 Set_Ignore_Errors (To => False);
1436 <<Continue>>
1437 null;
1438 end if;
1439 end loop Def_Line_Loop;
1440 end Parse_Def_File;
1442 end Prep;