Update LOCAL_PATCHES after libsanitizer merge.
[official-gcc.git] / gcc / ada / prep.adb
blob241c3cca4a56dc037292fb2b61a3cecfa5ccaa0c
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT COMPILER COMPONENTS --
4 -- --
5 -- P R E P --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2002-2018, 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 3, 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 COPYING3. If not, go to --
19 -- http://www.gnu.org/licenses for a complete copy of the license. --
20 -- --
21 -- GNAT was originally developed by the GNAT team at New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
23 -- --
24 ------------------------------------------------------------------------------
26 with Csets; use Csets;
27 with Err_Vars; use Err_Vars;
28 with Opt; use Opt;
29 with Osint; use Osint;
30 with Output; use Output;
31 with Scans; use Scans;
32 with Snames; use Snames;
33 with Sinput;
34 with Stringt; use Stringt;
35 with Table;
36 with Uintp; use Uintp;
38 with GNAT.Heap_Sort_G;
40 package body Prep is
42 use Symbol_Table;
44 type Token_Name_Array is array (Token_Type) of Name_Id;
45 Token_Names : constant Token_Name_Array :=
46 (Tok_Abort => Name_Abort,
47 Tok_Abs => Name_Abs,
48 Tok_Abstract => Name_Abstract,
49 Tok_Accept => Name_Accept,
50 Tok_Aliased => Name_Aliased,
51 Tok_All => Name_All,
52 Tok_Array => Name_Array,
53 Tok_And => Name_And,
54 Tok_At => Name_At,
55 Tok_Begin => Name_Begin,
56 Tok_Body => Name_Body,
57 Tok_Case => Name_Case,
58 Tok_Constant => Name_Constant,
59 Tok_Declare => Name_Declare,
60 Tok_Delay => Name_Delay,
61 Tok_Delta => Name_Delta,
62 Tok_Digits => Name_Digits,
63 Tok_Else => Name_Else,
64 Tok_Elsif => Name_Elsif,
65 Tok_End => Name_End,
66 Tok_Entry => Name_Entry,
67 Tok_Exception => Name_Exception,
68 Tok_Exit => Name_Exit,
69 Tok_For => Name_For,
70 Tok_Function => Name_Function,
71 Tok_Generic => Name_Generic,
72 Tok_Goto => Name_Goto,
73 Tok_If => Name_If,
74 Tok_Is => Name_Is,
75 Tok_Limited => Name_Limited,
76 Tok_Loop => Name_Loop,
77 Tok_Mod => Name_Mod,
78 Tok_New => Name_New,
79 Tok_Null => Name_Null,
80 Tok_Of => Name_Of,
81 Tok_Or => Name_Or,
82 Tok_Others => Name_Others,
83 Tok_Out => Name_Out,
84 Tok_Package => Name_Package,
85 Tok_Pragma => Name_Pragma,
86 Tok_Private => Name_Private,
87 Tok_Procedure => Name_Procedure,
88 Tok_Protected => Name_Protected,
89 Tok_Raise => Name_Raise,
90 Tok_Range => Name_Range,
91 Tok_Record => Name_Record,
92 Tok_Rem => Name_Rem,
93 Tok_Renames => Name_Renames,
94 Tok_Requeue => Name_Requeue,
95 Tok_Return => Name_Return,
96 Tok_Reverse => Name_Reverse,
97 Tok_Select => Name_Select,
98 Tok_Separate => Name_Separate,
99 Tok_Subtype => Name_Subtype,
100 Tok_Tagged => Name_Tagged,
101 Tok_Task => Name_Task,
102 Tok_Terminate => Name_Terminate,
103 Tok_Then => Name_Then,
104 Tok_Type => Name_Type,
105 Tok_Until => Name_Until,
106 Tok_Use => Name_Use,
107 Tok_When => Name_When,
108 Tok_While => Name_While,
109 Tok_With => Name_With,
110 Tok_Xor => Name_Xor,
111 others => No_Name);
113 Already_Initialized : Boolean := False;
114 -- Used to avoid repetition of the part of the initialisation that needs
115 -- to be done only once.
117 Empty_String : String_Id;
118 -- "", as a string_id
120 String_False : String_Id;
121 -- "false", as a string_id
123 --------------
124 -- Behavior --
125 --------------
127 -- Accesses to procedure specified by procedure Initialize
129 Error_Msg : Error_Msg_Proc;
130 -- Report an error
132 Scan : Scan_Proc;
133 -- Scan one token
135 Set_Ignore_Errors : Set_Ignore_Errors_Proc;
136 -- Indicate if error should be taken into account
138 Put_Char : Put_Char_Proc;
139 -- Output one character
141 New_EOL : New_EOL_Proc;
142 -- Output an end of line indication
144 -------------------------------
145 -- State of the Preprocessor --
146 -------------------------------
148 type Pp_State is record
149 If_Ptr : Source_Ptr;
150 -- The location of the #if statement (used to flag #if with no
151 -- corresponding #end if, at the end).
153 Else_Ptr : Source_Ptr;
154 -- The location of the #else statement (used to detect multiple #else's)
156 Deleting : Boolean;
157 -- Set to True when the code should be deleted or commented out
159 Match_Seen : Boolean;
160 -- Set to True when a condition in an #if or an #elsif is True. Also set
161 -- to True if Deleting at the previous level is True. Used to decide if
162 -- Deleting should be set to True in a following #elsif or #else.
164 end record;
166 type Pp_Depth is new Nat;
168 Ground : constant Pp_Depth := 0;
170 package Pp_States is new Table.Table
171 (Table_Component_Type => Pp_State,
172 Table_Index_Type => Pp_Depth,
173 Table_Low_Bound => 1,
174 Table_Initial => 10,
175 Table_Increment => 100,
176 Table_Name => "Prep.Pp_States");
177 -- A stack of the states of the preprocessor, for nested #if
179 type Operator is (None, Op_Or, Op_And);
181 -----------------
182 -- Subprograms --
183 -----------------
185 function Deleting return Boolean;
186 -- Return True if code should be deleted or commented out
188 function Expression
189 (Evaluate_It : Boolean;
190 Complemented : Boolean := False) return Boolean;
191 -- Evaluate a condition in an #if or an #elsif statement. If Evaluate_It
192 -- is False, the condition is effectively evaluated, otherwise, only the
193 -- syntax is checked.
195 procedure Go_To_End_Of_Line;
196 -- Advance the scan pointer until we reach an end of line or the end of the
197 -- buffer.
199 function Matching_Strings (S1, S2 : String_Id) return Boolean;
200 -- Returns True if the two string parameters are equal (case insensitive)
202 ---------------------------------------
203 -- Change_Reserved_Keyword_To_Symbol --
204 ---------------------------------------
206 procedure Change_Reserved_Keyword_To_Symbol
207 (All_Keywords : Boolean := False)
209 New_Name : constant Name_Id := Token_Names (Token);
211 begin
212 if New_Name /= No_Name then
213 case Token is
214 when Tok_And
215 | Tok_Else
216 | Tok_Elsif
217 | Tok_End
218 | Tok_If
219 | Tok_Or
220 | Tok_Then
222 if All_Keywords then
223 Token := Tok_Identifier;
224 Token_Name := New_Name;
225 end if;
227 when others =>
228 Token := Tok_Identifier;
229 Token_Name := New_Name;
230 end case;
231 end if;
232 end Change_Reserved_Keyword_To_Symbol;
234 ------------------------------------------
235 -- Check_Command_Line_Symbol_Definition --
236 ------------------------------------------
238 procedure Check_Command_Line_Symbol_Definition
239 (Definition : String;
240 Data : out Symbol_Data)
242 Index : Natural := 0;
243 Result : Symbol_Data;
245 begin
246 -- Look for the character '='
248 for J in Definition'Range loop
249 if Definition (J) = '=' then
250 Index := J;
251 exit;
252 end if;
253 end loop;
255 -- If no character '=', then the value is True
257 if Index = 0 then
259 -- Put the symbol in the name buffer
261 Name_Len := Definition'Length;
262 Name_Buffer (1 .. Name_Len) := Definition;
263 Result := True_Value;
265 elsif Index = Definition'First then
266 Fail ("invalid symbol definition """ & Definition & """");
268 else
269 -- Put the symbol in the name buffer
271 Name_Len := Index - Definition'First;
272 Name_Buffer (1 .. Name_Len) :=
273 String'(Definition (Definition'First .. Index - 1));
275 -- Check the syntax of the value
277 if Definition (Index + 1) /= '"'
278 or else Definition (Definition'Last) /= '"'
279 then
280 for J in Index + 1 .. Definition'Last loop
281 case Definition (J) is
282 when '_' | '.' | '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' =>
283 null;
285 when others =>
286 Fail ("illegal value """
287 & Definition (Index + 1 .. Definition'Last)
288 & """");
289 end case;
290 end loop;
291 end if;
293 -- Even if the value is a string, we still set Is_A_String to False,
294 -- to avoid adding additional quotes in the preprocessed sources when
295 -- replacing $<symbol>.
297 Result.Is_A_String := False;
299 -- Put the value in the result
301 Start_String;
302 Store_String_Chars (Definition (Index + 1 .. Definition'Last));
303 Result.Value := End_String;
304 end if;
306 -- Now, check the syntax of the symbol (we don't allow accented or
307 -- wide characters).
309 if Name_Buffer (1) not in 'a' .. 'z'
310 and then Name_Buffer (1) not in 'A' .. 'Z'
311 then
312 Fail ("symbol """
313 & Name_Buffer (1 .. Name_Len)
314 & """ does not start with a letter");
315 end if;
317 for J in 2 .. Name_Len loop
318 case Name_Buffer (J) is
319 when 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' =>
320 null;
322 when '_' =>
323 if J = Name_Len then
324 Fail ("symbol """
325 & Name_Buffer (1 .. Name_Len)
326 & """ end with a '_'");
328 elsif Name_Buffer (J + 1) = '_' then
329 Fail ("symbol """
330 & Name_Buffer (1 .. Name_Len)
331 & """ contains consecutive '_'");
332 end if;
334 when others =>
335 Fail ("symbol """
336 & Name_Buffer (1 .. Name_Len)
337 & """ contains illegal character(s)");
338 end case;
339 end loop;
341 Result.On_The_Command_Line := True;
343 -- Put the symbol name in the result
345 declare
346 Sym : constant String := Name_Buffer (1 .. Name_Len);
348 begin
349 for Index in 1 .. Name_Len loop
350 Name_Buffer (Index) := Fold_Lower (Name_Buffer (Index));
351 end loop;
353 Result.Symbol := Name_Find;
354 Name_Len := Sym'Length;
355 Name_Buffer (1 .. Name_Len) := Sym;
356 Result.Original := Name_Find;
357 end;
359 Data := Result;
360 end Check_Command_Line_Symbol_Definition;
362 --------------
363 -- Deleting --
364 --------------
366 function Deleting return Boolean is
367 begin
368 -- Always return False when not inside an #if statement
370 if Opt.No_Deletion or else Pp_States.Last = Ground then
371 return False;
372 else
373 return Pp_States.Table (Pp_States.Last).Deleting;
374 end if;
375 end Deleting;
377 ----------------
378 -- Expression --
379 ----------------
381 function Expression
382 (Evaluate_It : Boolean;
383 Complemented : Boolean := False) return Boolean
385 Evaluation : Boolean := Evaluate_It;
386 -- Is set to False after an "or else" when left term is True and after
387 -- an "and then" when left term is False.
389 Final_Result : Boolean := False;
391 Current_Result : Boolean := False;
392 -- Value of a term
394 Current_Operator : Operator := None;
395 Symbol1 : Symbol_Id;
396 Symbol2 : Symbol_Id;
397 Symbol_Name1 : Name_Id;
398 Symbol_Name2 : Name_Id;
399 Symbol_Pos1 : Source_Ptr;
400 Symbol_Pos2 : Source_Ptr;
401 Symbol_Value1 : String_Id;
402 Symbol_Value2 : String_Id;
404 Relop : Token_Type;
406 begin
407 -- Loop for each term
409 loop
410 Change_Reserved_Keyword_To_Symbol;
412 Current_Result := False;
414 -- Scan current term, starting with Token
416 case Token is
418 -- Handle parenthesized expression
420 when Tok_Left_Paren =>
421 Scan.all;
422 Current_Result := Expression (Evaluation);
424 if Token = Tok_Right_Paren then
425 Scan.all;
427 else
428 Error_Msg -- CODEFIX
429 ("`)` expected", Token_Ptr);
430 end if;
432 -- Handle not expression
434 when Tok_Not =>
435 Scan.all;
436 Current_Result :=
437 not Expression (Evaluation, Complemented => True);
439 -- Handle sequence starting with identifier
441 when Tok_Identifier =>
442 Symbol_Name1 := Token_Name;
443 Symbol_Pos1 := Token_Ptr;
444 Scan.all;
446 if Token = Tok_Apostrophe then
448 -- symbol'Defined
450 Scan.all;
452 if Token = Tok_Identifier
453 and then Token_Name = Name_Defined
454 then
455 Scan.all;
457 else
458 Error_Msg ("identifier `Defined` expected", Token_Ptr);
459 end if;
461 if Evaluation then
462 Current_Result := Index_Of (Symbol_Name1) /= No_Symbol;
463 end if;
465 -- Handle relational operator
467 elsif Token = Tok_Equal
468 or else Token = Tok_Less
469 or else Token = Tok_Less_Equal
470 or else Token = Tok_Greater
471 or else Token = Tok_Greater_Equal
472 then
473 Relop := Token;
474 Scan.all;
475 Change_Reserved_Keyword_To_Symbol;
477 if Token = Tok_Integer_Literal then
479 -- symbol = integer
480 -- symbol < integer
481 -- symbol <= integer
482 -- symbol > integer
483 -- symbol >= integer
485 declare
486 Value : constant Int := UI_To_Int (Int_Literal_Value);
487 Data : Symbol_Data;
489 Symbol_Value : Int;
490 -- Value of symbol as Int
492 begin
493 if Evaluation then
494 Symbol1 := Index_Of (Symbol_Name1);
496 if Symbol1 = No_Symbol then
497 Error_Msg_Name_1 := Symbol_Name1;
498 Error_Msg ("unknown symbol %", Symbol_Pos1);
499 Symbol_Value1 := No_String;
501 else
502 Data := Mapping.Table (Symbol1);
504 if Data.Is_A_String then
505 Error_Msg_Name_1 := Symbol_Name1;
506 Error_Msg
507 ("symbol % value is not integer",
508 Symbol_Pos1);
510 else
511 begin
512 String_To_Name_Buffer (Data.Value);
513 Symbol_Value :=
514 Int'Value (Name_Buffer (1 .. Name_Len));
516 case Relop is
517 when Tok_Equal =>
518 Current_Result :=
519 Symbol_Value = Value;
521 when Tok_Less =>
522 Current_Result :=
523 Symbol_Value < Value;
525 when Tok_Less_Equal =>
526 Current_Result :=
527 Symbol_Value <= Value;
529 when Tok_Greater =>
530 Current_Result :=
531 Symbol_Value > Value;
533 when Tok_Greater_Equal =>
534 Current_Result :=
535 Symbol_Value >= Value;
537 when others =>
538 null;
539 end case;
541 exception
542 when Constraint_Error =>
543 Error_Msg_Name_1 := Symbol_Name1;
544 Error_Msg
545 ("symbol % value is not an integer",
546 Symbol_Pos1);
547 end;
548 end if;
549 end if;
550 end if;
552 Scan.all;
553 end;
555 -- Error if relational operator other than = if not numbers
557 elsif Relop /= Tok_Equal then
558 Error_Msg ("number expected", Token_Ptr);
560 -- Equality comparison of two strings
562 elsif Token = Tok_Identifier then
564 -- symbol = symbol
566 Symbol_Name2 := Token_Name;
567 Symbol_Pos2 := Token_Ptr;
568 Scan.all;
570 if Evaluation then
571 Symbol1 := Index_Of (Symbol_Name1);
573 if Symbol1 = No_Symbol then
574 if Undefined_Symbols_Are_False then
575 Symbol_Value1 := String_False;
577 else
578 Error_Msg_Name_1 := Symbol_Name1;
579 Error_Msg ("unknown symbol %", Symbol_Pos1);
580 Symbol_Value1 := No_String;
581 end if;
583 else
584 Symbol_Value1 :=
585 Mapping.Table (Symbol1).Value;
586 end if;
588 Symbol2 := Index_Of (Symbol_Name2);
590 if Symbol2 = No_Symbol then
591 if Undefined_Symbols_Are_False then
592 Symbol_Value2 := String_False;
594 else
595 Error_Msg_Name_1 := Symbol_Name2;
596 Error_Msg ("unknown symbol %", Symbol_Pos2);
597 Symbol_Value2 := No_String;
598 end if;
600 else
601 Symbol_Value2 := Mapping.Table (Symbol2).Value;
602 end if;
604 if Symbol_Value1 /= No_String
605 and then
606 Symbol_Value2 /= No_String
607 then
608 Current_Result :=
609 Matching_Strings (Symbol_Value1, Symbol_Value2);
610 end if;
611 end if;
613 elsif Token = Tok_String_Literal then
615 -- symbol = "value"
617 if Evaluation then
618 Symbol1 := Index_Of (Symbol_Name1);
620 if Symbol1 = No_Symbol then
621 if Undefined_Symbols_Are_False then
622 Symbol_Value1 := String_False;
624 else
625 Error_Msg_Name_1 := Symbol_Name1;
626 Error_Msg ("unknown symbol %", Symbol_Pos1);
627 Symbol_Value1 := No_String;
628 end if;
630 else
631 Symbol_Value1 := Mapping.Table (Symbol1).Value;
632 end if;
634 if Symbol_Value1 /= No_String then
635 Current_Result :=
636 Matching_Strings
637 (Symbol_Value1,
638 String_Literal_Id);
639 end if;
640 end if;
642 Scan.all;
644 else
645 Error_Msg
646 ("literal integer, symbol or literal string expected",
647 Token_Ptr);
648 end if;
650 -- Handle True or False
652 else
653 if Evaluation then
654 Symbol1 := Index_Of (Symbol_Name1);
656 if Symbol1 = No_Symbol then
657 if Undefined_Symbols_Are_False then
658 Symbol_Value1 := String_False;
660 else
661 Error_Msg_Name_1 := Symbol_Name1;
662 Error_Msg ("unknown symbol %", Symbol_Pos1);
663 Symbol_Value1 := No_String;
664 end if;
666 else
667 Symbol_Value1 := Mapping.Table (Symbol1).Value;
668 end if;
670 if Symbol_Value1 /= No_String then
671 String_To_Name_Buffer (Symbol_Value1);
673 for Index in 1 .. Name_Len loop
674 Name_Buffer (Index) :=
675 Fold_Lower (Name_Buffer (Index));
676 end loop;
678 if Name_Buffer (1 .. Name_Len) = "true" then
679 Current_Result := True;
681 elsif Name_Buffer (1 .. Name_Len) = "false" then
682 Current_Result := False;
684 else
685 Error_Msg_Name_1 := Symbol_Name1;
686 Error_Msg
687 ("value of symbol % is not True or False",
688 Symbol_Pos1);
689 end if;
690 end if;
691 end if;
692 end if;
694 -- Unrecognized sequence
696 when others =>
697 Error_Msg ("`(`, NOT or symbol expected", Token_Ptr);
698 end case;
700 -- Update the cumulative final result
702 case Current_Operator is
703 when None =>
704 Final_Result := Current_Result;
706 when Op_Or =>
707 Final_Result := Final_Result or Current_Result;
709 when Op_And =>
710 Final_Result := Final_Result and Current_Result;
711 end case;
713 -- Handle AND
715 if Token = Tok_And then
716 if Complemented then
717 Error_Msg
718 ("mixing NOT and AND is not allowed, parentheses are required",
719 Token_Ptr);
721 elsif Current_Operator = Op_Or then
722 Error_Msg ("mixing OR and AND is not allowed", Token_Ptr);
723 end if;
725 Current_Operator := Op_And;
726 Scan.all;
728 if Token = Tok_Then then
729 Scan.all;
731 if Final_Result = False then
732 Evaluation := False;
733 end if;
734 end if;
736 -- Handle OR
738 elsif Token = Tok_Or then
739 if Complemented then
740 Error_Msg
741 ("mixing NOT and OR is not allowed, parentheses are required",
742 Token_Ptr);
744 elsif Current_Operator = Op_And then
745 Error_Msg ("mixing AND and OR is not allowed", Token_Ptr);
746 end if;
748 Current_Operator := Op_Or;
749 Scan.all;
751 if Token = Tok_Else then
752 Scan.all;
754 if Final_Result then
755 Evaluation := False;
756 end if;
757 end if;
759 -- No AND/OR operator, so exit from the loop through terms
761 else
762 exit;
763 end if;
764 end loop;
766 return Final_Result;
767 end Expression;
769 -----------------------
770 -- Go_To_End_Of_Line --
771 -----------------------
773 procedure Go_To_End_Of_Line is
774 begin
775 -- Scan until we get an end of line or we reach the end of the buffer
777 while Token /= Tok_End_Of_Line
778 and then Token /= Tok_EOF
779 loop
780 Scan.all;
781 end loop;
782 end Go_To_End_Of_Line;
784 --------------
785 -- Index_Of --
786 --------------
788 function Index_Of (Symbol : Name_Id) return Symbol_Id is
789 begin
790 if Mapping.Table /= null then
791 for J in Symbol_Id range 1 .. Symbol_Table.Last (Mapping) loop
792 if Mapping.Table (J).Symbol = Symbol then
793 return J;
794 end if;
795 end loop;
796 end if;
798 return No_Symbol;
799 end Index_Of;
801 ----------------
802 -- Initialize --
803 ----------------
805 procedure Initialize is
806 begin
807 if not Already_Initialized then
808 Start_String;
809 Store_String_Chars ("True");
810 True_Value.Value := End_String;
812 Start_String;
813 Empty_String := End_String;
815 Start_String;
816 Store_String_Chars ("False");
817 String_False := End_String;
819 Already_Initialized := True;
820 end if;
821 end Initialize;
823 ------------------
824 -- List_Symbols --
825 ------------------
827 procedure List_Symbols (Foreword : String) is
828 Order : array (0 .. Integer (Symbol_Table.Last (Mapping)))
829 of Symbol_Id;
830 -- After alphabetical sorting, this array stores the indexes of the
831 -- symbols in the order they are displayed.
833 function Lt (Op1, Op2 : Natural) return Boolean;
834 -- Comparison routine for sort call
836 procedure Move (From : Natural; To : Natural);
837 -- Move routine for sort call
839 --------
840 -- Lt --
841 --------
843 function Lt (Op1, Op2 : Natural) return Boolean is
844 S1 : constant String :=
845 Get_Name_String (Mapping.Table (Order (Op1)).Symbol);
846 S2 : constant String :=
847 Get_Name_String (Mapping.Table (Order (Op2)).Symbol);
848 begin
849 return S1 < S2;
850 end Lt;
852 ----------
853 -- Move --
854 ----------
856 procedure Move (From : Natural; To : Natural) is
857 begin
858 Order (To) := Order (From);
859 end Move;
861 package Sort_Syms is new GNAT.Heap_Sort_G (Move, Lt);
863 Max_L : Natural;
864 -- Maximum length of any symbol
866 -- Start of processing for List_Symbols_Case
868 begin
869 if Symbol_Table.Last (Mapping) = 0 then
870 return;
871 end if;
873 if Foreword'Length > 0 then
874 Write_Eol;
875 Write_Line (Foreword);
877 for J in Foreword'Range loop
878 Write_Char ('=');
879 end loop;
880 end if;
882 -- Initialize the order
884 for J in Order'Range loop
885 Order (J) := Symbol_Id (J);
886 end loop;
888 -- Sort alphabetically
890 Sort_Syms.Sort (Order'Last);
892 Max_L := 7;
894 for J in 1 .. Symbol_Table.Last (Mapping) loop
895 Get_Name_String (Mapping.Table (J).Original);
896 Max_L := Integer'Max (Max_L, Name_Len);
897 end loop;
899 Write_Eol;
900 Write_Str ("Symbol");
902 for J in 1 .. Max_L - 5 loop
903 Write_Char (' ');
904 end loop;
906 Write_Line ("Value");
908 Write_Str ("------");
910 for J in 1 .. Max_L - 5 loop
911 Write_Char (' ');
912 end loop;
914 Write_Line ("------");
916 for J in 1 .. Order'Last loop
917 declare
918 Data : constant Symbol_Data := Mapping.Table (Order (J));
920 begin
921 Get_Name_String (Data.Original);
922 Write_Str (Name_Buffer (1 .. Name_Len));
924 for K in Name_Len .. Max_L loop
925 Write_Char (' ');
926 end loop;
928 String_To_Name_Buffer (Data.Value);
930 if Data.Is_A_String then
931 Write_Char ('"');
933 for J in 1 .. Name_Len loop
934 Write_Char (Name_Buffer (J));
936 if Name_Buffer (J) = '"' then
937 Write_Char ('"');
938 end if;
939 end loop;
941 Write_Char ('"');
943 else
944 Write_Str (Name_Buffer (1 .. Name_Len));
945 end if;
946 end;
948 Write_Eol;
949 end loop;
951 Write_Eol;
952 end List_Symbols;
954 ----------------------
955 -- Matching_Strings --
956 ----------------------
958 function Matching_Strings (S1, S2 : String_Id) return Boolean is
959 begin
960 String_To_Name_Buffer (S1);
962 for Index in 1 .. Name_Len loop
963 Name_Buffer (Index) := Fold_Lower (Name_Buffer (Index));
964 end loop;
966 declare
967 String1 : constant String := Name_Buffer (1 .. Name_Len);
969 begin
970 String_To_Name_Buffer (S2);
972 for Index in 1 .. Name_Len loop
973 Name_Buffer (Index) := Fold_Lower (Name_Buffer (Index));
974 end loop;
976 return String1 = Name_Buffer (1 .. Name_Len);
977 end;
978 end Matching_Strings;
980 --------------------
981 -- Parse_Def_File --
982 --------------------
984 -- This procedure REALLY needs some more comments ???
986 procedure Parse_Def_File is
987 Symbol : Symbol_Id;
988 Symbol_Name : Name_Id;
989 Original_Name : Name_Id;
990 Data : Symbol_Data;
991 Value_Start : Source_Ptr;
992 Value_End : Source_Ptr;
993 Ch : Character;
995 use ASCII;
997 begin
998 Def_Line_Loop :
999 loop
1000 Scan.all;
1002 exit Def_Line_Loop when Token = Tok_EOF;
1004 if Token /= Tok_End_Of_Line then
1005 Change_Reserved_Keyword_To_Symbol;
1007 if Token /= Tok_Identifier then
1008 Error_Msg ("identifier expected", Token_Ptr);
1009 goto Cleanup;
1010 end if;
1012 Symbol_Name := Token_Name;
1013 Name_Len := 0;
1015 for Ptr in Token_Ptr .. Scan_Ptr - 1 loop
1016 Name_Len := Name_Len + 1;
1017 Name_Buffer (Name_Len) := Sinput.Source (Ptr);
1018 end loop;
1020 Original_Name := Name_Find;
1021 Scan.all;
1023 if Token /= Tok_Colon_Equal then
1024 Error_Msg -- CODEFIX
1025 ("`:=` expected", Token_Ptr);
1026 goto Cleanup;
1027 end if;
1029 Scan.all;
1031 if Token = Tok_Integer_Literal then
1032 declare
1033 Ptr : Source_Ptr := Token_Ptr;
1035 begin
1036 Start_String;
1037 while Ptr < Scan_Ptr loop
1038 Store_String_Char (Sinput.Source (Ptr));
1039 Ptr := Ptr + 1;
1040 end loop;
1042 Data := (Symbol => Symbol_Name,
1043 Original => Original_Name,
1044 On_The_Command_Line => False,
1045 Is_A_String => False,
1046 Value => End_String);
1047 end;
1049 Scan.all;
1051 if Token /= Tok_End_Of_Line and then Token /= Tok_EOF then
1052 Error_Msg ("extraneous text in definition", Token_Ptr);
1053 goto Cleanup;
1054 end if;
1056 elsif Token = Tok_String_Literal then
1057 Data := (Symbol => Symbol_Name,
1058 Original => Original_Name,
1059 On_The_Command_Line => False,
1060 Is_A_String => True,
1061 Value => String_Literal_Id);
1063 Scan.all;
1065 if Token /= Tok_End_Of_Line and then Token /= Tok_EOF then
1066 Error_Msg ("extraneous text in definition", Token_Ptr);
1067 goto Cleanup;
1068 end if;
1070 elsif Token = Tok_End_Of_Line or else Token = Tok_EOF then
1071 Data := (Symbol => Symbol_Name,
1072 Original => Original_Name,
1073 On_The_Command_Line => False,
1074 Is_A_String => False,
1075 Value => Empty_String);
1077 else
1078 Value_Start := Token_Ptr;
1079 Value_End := Token_Ptr - 1;
1080 Scan_Ptr := Token_Ptr;
1082 Value_Chars_Loop :
1083 loop
1084 Ch := Sinput.Source (Scan_Ptr);
1086 case Ch is
1087 when '_' | '.' | '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' =>
1088 Value_End := Scan_Ptr;
1089 Scan_Ptr := Scan_Ptr + 1;
1091 when ' ' | HT | VT | CR | LF | FF =>
1092 exit Value_Chars_Loop;
1094 when others =>
1095 Error_Msg ("illegal character", Scan_Ptr);
1096 goto Cleanup;
1097 end case;
1098 end loop Value_Chars_Loop;
1100 Scan.all;
1102 if Token /= Tok_End_Of_Line and then Token /= Tok_EOF then
1103 Error_Msg ("extraneous text in definition", Token_Ptr);
1104 goto Cleanup;
1105 end if;
1107 Start_String;
1109 while Value_Start <= Value_End loop
1110 Store_String_Char (Sinput.Source (Value_Start));
1111 Value_Start := Value_Start + 1;
1112 end loop;
1114 Data := (Symbol => Symbol_Name,
1115 Original => Original_Name,
1116 On_The_Command_Line => False,
1117 Is_A_String => False,
1118 Value => End_String);
1119 end if;
1121 -- Now that we have the value, get the symbol index
1123 Symbol := Index_Of (Symbol_Name);
1125 if Symbol /= No_Symbol then
1127 -- If we already have an entry for this symbol, replace it
1128 -- with the new value, except if the symbol was declared on
1129 -- the command line.
1131 if Mapping.Table (Symbol).On_The_Command_Line then
1132 goto Continue;
1133 end if;
1135 else
1136 -- As it is the first time we see this symbol, create a new
1137 -- entry in the table.
1139 if Mapping.Table = null then
1140 Symbol_Table.Init (Mapping);
1141 end if;
1143 Symbol_Table.Increment_Last (Mapping);
1144 Symbol := Symbol_Table.Last (Mapping);
1145 end if;
1147 Mapping.Table (Symbol) := Data;
1148 goto Continue;
1150 <<Cleanup>>
1151 Set_Ignore_Errors (To => True);
1153 while Token /= Tok_End_Of_Line and then Token /= Tok_EOF loop
1154 Scan.all;
1155 end loop;
1157 Set_Ignore_Errors (To => False);
1159 <<Continue>>
1160 null;
1161 end if;
1162 end loop Def_Line_Loop;
1163 end Parse_Def_File;
1165 ----------------
1166 -- Preprocess --
1167 ----------------
1169 procedure Preprocess (Source_Modified : out Boolean) is
1170 Start_Of_Processing : Source_Ptr;
1171 Cond : Boolean;
1172 Preprocessor_Line : Boolean := False;
1173 No_Error_Found : Boolean := True;
1174 Modified : Boolean := False;
1176 procedure Output (From, To : Source_Ptr);
1177 -- Output the characters with indexes From .. To in the buffer to the
1178 -- output file.
1180 procedure Output_Line (From, To : Source_Ptr);
1181 -- Output a line or the end of a line from the buffer to the output
1182 -- file, followed by an end of line terminator. Depending on the value
1183 -- of Deleting and the switches, the line may be commented out, blank or
1184 -- not output at all.
1186 ------------
1187 -- Output --
1188 ------------
1190 procedure Output (From, To : Source_Ptr) is
1191 begin
1192 for J in From .. To loop
1193 Put_Char (Sinput.Source (J));
1194 end loop;
1195 end Output;
1197 -----------------
1198 -- Output_Line --
1199 -----------------
1201 procedure Output_Line (From, To : Source_Ptr) is
1202 begin
1203 if Deleting or else Preprocessor_Line then
1204 if Blank_Deleted_Lines then
1205 New_EOL.all;
1207 elsif Comment_Deleted_Lines then
1208 Put_Char ('-');
1209 Put_Char ('-');
1210 Put_Char ('!');
1212 if From < To then
1213 Put_Char (' ');
1214 Output (From, To);
1215 end if;
1217 New_EOL.all;
1218 end if;
1220 else
1221 Output (From, To);
1222 New_EOL.all;
1223 end if;
1224 end Output_Line;
1226 -- Start of processing for Preprocess
1228 begin
1229 Start_Of_Processing := Scan_Ptr;
1231 -- First a call to Scan, because Initialize_Scanner is not doing it
1233 Scan.all;
1235 Input_Line_Loop : loop
1236 exit Input_Line_Loop when Token = Tok_EOF;
1238 Preprocessor_Line := False;
1240 if Token /= Tok_End_Of_Line then
1242 -- Preprocessor line
1244 if Token = Tok_Special and then Special_Character = '#' then
1245 Modified := True;
1246 Preprocessor_Line := True;
1247 Scan.all;
1249 case Token is
1251 -- #if
1253 when Tok_If =>
1254 declare
1255 If_Ptr : constant Source_Ptr := Token_Ptr;
1257 begin
1258 Scan.all;
1259 Cond := Expression (not Deleting);
1261 -- Check for an eventual "then"
1263 if Token = Tok_Then then
1264 Scan.all;
1265 end if;
1267 -- It is an error to have trailing characters after
1268 -- the condition or "then".
1270 if Token /= Tok_End_Of_Line
1271 and then Token /= Tok_EOF
1272 then
1273 Error_Msg
1274 ("extraneous text on preprocessor line",
1275 Token_Ptr);
1276 No_Error_Found := False;
1277 Go_To_End_Of_Line;
1278 end if;
1280 declare
1281 -- Set the initial state of this new "#if". This
1282 -- must be done before incrementing the Last of
1283 -- the table, otherwise function Deleting does
1284 -- not report the correct value.
1286 New_State : constant Pp_State :=
1287 (If_Ptr => If_Ptr,
1288 Else_Ptr => 0,
1289 Deleting => Deleting
1290 or else not Cond,
1291 Match_Seen => Deleting or else Cond);
1293 begin
1294 Pp_States.Increment_Last;
1295 Pp_States.Table (Pp_States.Last) := New_State;
1296 end;
1297 end;
1299 -- #elsif
1301 when Tok_Elsif =>
1302 Cond := False;
1304 if Pp_States.Last = 0
1305 or else Pp_States.Table (Pp_States.Last).Else_Ptr /= 0
1306 then
1307 Error_Msg ("no IF for this ELSIF", Token_Ptr);
1308 No_Error_Found := False;
1310 else
1311 Cond :=
1312 not Pp_States.Table (Pp_States.Last).Match_Seen;
1313 end if;
1315 Scan.all;
1316 Cond := Expression (Cond);
1318 -- Check for an eventual "then"
1320 if Token = Tok_Then then
1321 Scan.all;
1322 end if;
1324 -- It is an error to have trailing characters after the
1325 -- condition or "then".
1327 if Token /= Tok_End_Of_Line
1328 and then Token /= Tok_EOF
1329 then
1330 Error_Msg
1331 ("extraneous text on preprocessor line",
1332 Token_Ptr);
1333 No_Error_Found := False;
1335 Go_To_End_Of_Line;
1336 end if;
1338 -- Depending on the value of the condition, set the new
1339 -- values of Deleting and Match_Seen.
1341 if Pp_States.Last > 0 then
1342 if Pp_States.Table (Pp_States.Last).Match_Seen then
1343 Pp_States.Table (Pp_States.Last).Deleting := True;
1344 else
1345 if Cond then
1346 Pp_States.Table (Pp_States.Last).Match_Seen :=
1347 True;
1348 Pp_States.Table (Pp_States.Last).Deleting :=
1349 False;
1350 end if;
1351 end if;
1352 end if;
1354 -- #else
1356 when Tok_Else =>
1357 if Pp_States.Last = 0 then
1358 Error_Msg ("no IF for this ELSE", Token_Ptr);
1359 No_Error_Found := False;
1361 elsif
1362 Pp_States.Table (Pp_States.Last).Else_Ptr /= 0
1363 then
1364 Error_Msg -- CODEFIX
1365 ("duplicate ELSE line", Token_Ptr);
1366 No_Error_Found := False;
1367 end if;
1369 -- Set the possibly new values of Deleting and Match_Seen
1371 if Pp_States.Last > 0 then
1372 if Pp_States.Table (Pp_States.Last).Match_Seen then
1373 Pp_States.Table (Pp_States.Last).Deleting :=
1374 True;
1376 else
1377 Pp_States.Table (Pp_States.Last).Match_Seen :=
1378 True;
1379 Pp_States.Table (Pp_States.Last).Deleting :=
1380 False;
1381 end if;
1383 -- Set the Else_Ptr to check for illegal #elsif later
1385 Pp_States.Table (Pp_States.Last).Else_Ptr :=
1386 Token_Ptr;
1387 end if;
1389 Scan.all;
1391 -- Error of character present after "#else"
1393 if Token /= Tok_End_Of_Line
1394 and then Token /= Tok_EOF
1395 then
1396 Error_Msg
1397 ("extraneous text on preprocessor line",
1398 Token_Ptr);
1399 No_Error_Found := False;
1400 Go_To_End_Of_Line;
1401 end if;
1403 -- #end if;
1405 when Tok_End =>
1406 if Pp_States.Last = 0 then
1407 Error_Msg ("no IF for this END", Token_Ptr);
1408 No_Error_Found := False;
1409 end if;
1411 Scan.all;
1413 if Token /= Tok_If then
1414 Error_Msg -- CODEFIX
1415 ("IF expected", Token_Ptr);
1416 No_Error_Found := False;
1418 else
1419 Scan.all;
1421 if Token /= Tok_Semicolon then
1422 Error_Msg -- CODEFIX
1423 ("`;` Expected", Token_Ptr);
1424 No_Error_Found := False;
1426 else
1427 Scan.all;
1429 -- Error of character present after "#end if;"
1431 if Token /= Tok_End_Of_Line
1432 and then Token /= Tok_EOF
1433 then
1434 Error_Msg
1435 ("extraneous text on preprocessor line",
1436 Token_Ptr);
1437 No_Error_Found := False;
1438 end if;
1439 end if;
1440 end if;
1442 -- In case of one of the errors above, skip the tokens
1443 -- until the end of line is reached.
1445 Go_To_End_Of_Line;
1447 -- Decrement the depth of the #if stack
1449 if Pp_States.Last > 0 then
1450 Pp_States.Decrement_Last;
1451 end if;
1453 -- Illegal preprocessor line
1455 when others =>
1456 No_Error_Found := False;
1458 if Pp_States.Last = 0 then
1459 Error_Msg -- CODEFIX
1460 ("IF expected", Token_Ptr);
1462 elsif
1463 Pp_States.Table (Pp_States.Last).Else_Ptr = 0
1464 then
1465 Error_Msg
1466 ("IF, ELSIF, ELSE, or `END IF` expected",
1467 Token_Ptr);
1469 else
1470 Error_Msg ("IF or `END IF` expected", Token_Ptr);
1471 end if;
1473 -- Skip to the end of this illegal line
1475 Go_To_End_Of_Line;
1476 end case;
1478 -- Not a preprocessor line
1480 else
1481 -- Do not report errors for those lines, even if there are
1482 -- Ada parsing errors.
1484 Set_Ignore_Errors (To => True);
1486 if Deleting then
1487 Go_To_End_Of_Line;
1489 else
1490 while Token /= Tok_End_Of_Line
1491 and then Token /= Tok_EOF
1492 loop
1493 if Token = Tok_Special
1494 and then Special_Character = '$'
1495 then
1496 Modified := True;
1498 declare
1499 Dollar_Ptr : constant Source_Ptr := Token_Ptr;
1500 Symbol : Symbol_Id;
1502 begin
1503 Scan.all;
1504 Change_Reserved_Keyword_To_Symbol;
1506 if Token = Tok_Identifier
1507 and then Token_Ptr = Dollar_Ptr + 1
1508 then
1509 -- $symbol
1511 Symbol := Index_Of (Token_Name);
1513 -- If symbol exists, replace by its value
1515 if Symbol /= No_Symbol then
1516 Output (Start_Of_Processing, Dollar_Ptr - 1);
1517 Start_Of_Processing := Scan_Ptr;
1518 String_To_Name_Buffer
1519 (Mapping.Table (Symbol).Value);
1521 if Mapping.Table (Symbol).Is_A_String then
1523 -- Value is an Ada string
1525 Put_Char ('"');
1527 for J in 1 .. Name_Len loop
1528 Put_Char (Name_Buffer (J));
1530 if Name_Buffer (J) = '"' then
1531 Put_Char ('"');
1532 end if;
1533 end loop;
1535 Put_Char ('"');
1537 else
1538 -- Value is a sequence of characters, not
1539 -- an Ada string.
1541 for J in 1 .. Name_Len loop
1542 Put_Char (Name_Buffer (J));
1543 end loop;
1544 end if;
1545 end if;
1546 end if;
1547 end;
1548 end if;
1550 Scan.all;
1551 end loop;
1552 end if;
1554 Set_Ignore_Errors (To => False);
1555 end if;
1556 end if;
1558 pragma Assert (Token = Tok_End_Of_Line or else Token = Tok_EOF);
1560 -- At this point, the token is either end of line or EOF. The line to
1561 -- possibly output stops just before the token.
1563 Output_Line (Start_Of_Processing, Token_Ptr - 1);
1565 -- If we are at the end of a line, the scan pointer is at the first
1566 -- non-blank character (may not be the first character of the line),
1567 -- so we have to deduct Start_Of_Processing from the token pointer.
1569 if Token = Tok_End_Of_Line then
1570 if Sinput.Source (Token_Ptr) = ASCII.CR
1571 and then Sinput.Source (Token_Ptr + 1) = ASCII.LF
1572 then
1573 Start_Of_Processing := Token_Ptr + 2;
1574 else
1575 Start_Of_Processing := Token_Ptr + 1;
1576 end if;
1577 end if;
1579 -- Now, scan the first token of the next line. If the token is EOF,
1580 -- the scan pointer will not move, and the token will still be EOF.
1582 Set_Ignore_Errors (To => True);
1583 Scan.all;
1584 Set_Ignore_Errors (To => False);
1585 end loop Input_Line_Loop;
1587 -- Report an error for any missing some "#end if;"
1589 for Level in reverse 1 .. Pp_States.Last loop
1590 Error_Msg ("no `END IF` for this IF", Pp_States.Table (Level).If_Ptr);
1591 No_Error_Found := False;
1592 end loop;
1594 Source_Modified := No_Error_Found and Modified;
1595 end Preprocess;
1597 -----------------
1598 -- Setup_Hooks --
1599 -----------------
1601 procedure Setup_Hooks
1602 (Error_Msg : Error_Msg_Proc;
1603 Scan : Scan_Proc;
1604 Set_Ignore_Errors : Set_Ignore_Errors_Proc;
1605 Put_Char : Put_Char_Proc;
1606 New_EOL : New_EOL_Proc)
1608 begin
1609 pragma Assert (Already_Initialized);
1611 Prep.Error_Msg := Error_Msg;
1612 Prep.Scan := Scan;
1613 Prep.Set_Ignore_Errors := Set_Ignore_Errors;
1614 Prep.Put_Char := Put_Char;
1615 Prep.New_EOL := New_EOL;
1616 end Setup_Hooks;
1618 end Prep;