OSDN Git Service

2005-11-14 Robert Dewar <dewar@adacore.com>
[pf3gnuchains/gcc-fork.git] / gcc / ada / prep.adb
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 ------------------------------------------------------------------------------
26
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;
38
39 with GNAT.Heap_Sort_G;
40
41 package body Prep is
42
43    use Symbol_Table;
44
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);
113
114    Already_Initialized : Boolean := False;
115    --  Used to avoid repetition of the part of the initialisation that needs
116    --  to be done only once.
117
118    Empty_String : String_Id;
119    --  "", as a string_id
120
121    String_False : String_Id;
122    --  "false", as a string_id
123
124    Name_Defined : Name_Id;
125    --  defined, as a name_id
126
127    ---------------
128    -- Behaviour --
129    ---------------
130
131    --  Accesses to procedure specified by procedure Initialize
132
133    Error_Msg : Error_Msg_Proc;
134    --  Report an error
135
136    Scan : Scan_Proc;
137    --  Scan one token
138
139    Set_Ignore_Errors : Set_Ignore_Errors_Proc;
140    --  Indicate if error should be taken into account
141
142    Put_Char : Put_Char_Proc;
143    --  Output one character
144
145    New_EOL : New_EOL_Proc;
146    --  Output an end of line indication
147
148    -------------------------------
149    -- State of the Preprocessor --
150    -------------------------------
151
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.
156
157       Else_Ptr : Source_Ptr;
158       --  The location of the #else statement.
159       --  Used to detect multiple #else.
160
161       Deleting : Boolean;
162       --  Set to True when the code should be deleted or commented out
163
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.
169
170    end record;
171
172    type Pp_Depth is new Nat;
173
174    Ground : constant Pp_Depth := 0;
175
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
184
185    type Operator is (None, Op_Or, Op_And);
186
187    -----------------
188    -- Subprograms --
189    -----------------
190
191    function Deleting return Boolean;
192    --  Return True if code should be deleted or commented out
193
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.
198
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.
202
203    function Matching_Strings (S1, S2 : String_Id) return Boolean;
204    --  Returns True if the two string parameters are equal (case insensitive)
205
206    ---------------------------------------
207    -- Change_Reserved_Keyword_To_Symbol --
208    ---------------------------------------
209
210    procedure Change_Reserved_Keyword_To_Symbol
211      (All_Keywords : Boolean := False)
212    is
213       New_Name : constant Name_Id := Token_Names (Token);
214
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;
224
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;
231
232    ------------------------------------------
233    -- Check_Command_Line_Symbol_Definition --
234    ------------------------------------------
235
236    procedure Check_Command_Line_Symbol_Definition
237      (Definition  : String;
238       Data        : out Symbol_Data)
239    is
240       Index       : Natural := 0;
241       Result      : Symbol_Data;
242
243    begin
244       --  Look for the character '='
245
246       for J in Definition'Range loop
247          if Definition (J) = '=' then
248             Index := J;
249             exit;
250          end if;
251       end loop;
252
253       --  If no character '=', then the value is True
254
255       if Index = 0 then
256          --  Put the symbol in the name buffer
257
258          Name_Len := Definition'Length;
259          Name_Buffer (1 .. Name_Len) := Definition;
260          Result := True_Value;
261
262       elsif Index = Definition'First then
263          Fail ("invalid symbol definition """, Definition, """");
264
265       else
266          --  Put the symbol in the name buffer
267
268          Name_Len := Index - Definition'First;
269          Name_Buffer (1 .. Name_Len) :=
270            String'(Definition (Definition'First .. Index - 1));
271
272          --  Check the syntax of the value
273
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;
281
282                   when others =>
283                      Fail ("illegal value """,
284                            Definition (Index + 1 .. Definition'Last),
285                            """");
286                end case;
287             end loop;
288          end if;
289
290          --  And put the value in the result
291
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;
297
298       --  Now, check the syntax of the symbol (we don't allow accented and
299       --  wide characters)
300
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;
308
309       for J in 2 .. Name_Len loop
310          case Name_Buffer (J) is
311             when 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' =>
312                null;
313
314             when '_' =>
315                if J = Name_Len then
316                   Fail ("symbol """,
317                         Name_Buffer (1 .. Name_Len),
318                         """ end with a '_'");
319
320                elsif Name_Buffer (J + 1) = '_' then
321                   Fail ("symbol """,
322                         Name_Buffer (1 .. Name_Len),
323                         """ contains consecutive '_'");
324                end if;
325
326             when others =>
327                Fail ("symbol """,
328                      Name_Buffer (1 .. Name_Len),
329                      """ contains illegal character(s)");
330          end case;
331       end loop;
332
333       Result.On_The_Command_Line := True;
334
335       --  Put the symbol name in the result
336
337       declare
338          Sym : constant String := Name_Buffer (1 .. Name_Len);
339
340       begin
341          for Index in 1 .. Name_Len loop
342             Name_Buffer (Index) := Fold_Lower (Name_Buffer (Index));
343          end loop;
344
345          Result.Symbol := Name_Find;
346          Name_Len := Sym'Length;
347          Name_Buffer (1 .. Name_Len) := Sym;
348          Result.Original := Name_Find;
349       end;
350
351       Data := Result;
352    end Check_Command_Line_Symbol_Definition;
353
354    --------------
355    -- Deleting --
356    --------------
357
358    function Deleting return Boolean is
359    begin
360       --  Always return False when not inside an #if statement
361
362       if Pp_States.Last = Ground then
363          return False;
364
365       else
366          return Pp_States.Table (Pp_States.Last).Deleting;
367       end if;
368    end Deleting;
369
370    ----------------
371    -- Expression --
372    ----------------
373
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.
378
379       Final_Result : Boolean := False;
380
381       Current_Result : Boolean := False;
382       --  Value of a term
383
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;
393
394    begin
395       --  Loop for each term
396
397       loop
398          Change_Reserved_Keyword_To_Symbol;
399
400          Current_Result := False;
401
402          case Token is
403
404             when Tok_Left_Paren =>
405
406                --  ( expression )
407
408                Scan.all;
409                Current_Result := Expression (Evaluation);
410
411                if Token = Tok_Right_Paren then
412                   Scan.all;
413
414                else
415                   Error_Msg ("`)` expected", Token_Ptr);
416                end if;
417
418             when Tok_Not =>
419
420                --  not expression
421
422                Scan.all;
423                Current_Result := not Expression (Evaluation);
424
425             when Tok_Identifier =>
426                Symbol_Name1 := Token_Name;
427                Symbol_Pos1  := Token_Ptr;
428                Scan.all;
429
430                if Token = Tok_Apostrophe then
431
432                   --  symbol'Defined
433
434                   Scan.all;
435
436                   if Token = Tok_Identifier
437                     and then Token_Name = Name_Defined
438                   then
439                      Scan.all;
440
441                   else
442                      Error_Msg ("identifier `Defined` expected", Token_Ptr);
443                   end if;
444
445                   if Evaluation then
446                      Current_Result := Index_Of (Symbol_Name1) /= No_Symbol;
447                   end if;
448
449                elsif Token = Tok_Equal then
450                   Scan.all;
451
452                   Change_Reserved_Keyword_To_Symbol;
453
454                   if Token = Tok_Identifier then
455
456                      --  symbol = symbol
457
458                      Symbol_Name2 := Token_Name;
459                      Symbol_Pos2  := Token_Ptr;
460                      Scan.all;
461
462                      if Evaluation then
463                         Symbol1 := Index_Of (Symbol_Name1);
464
465                         if Symbol1 = No_Symbol then
466                            if Undefined_Symbols_Are_False then
467                               Symbol_Value1 := String_False;
468
469                            else
470                               Error_Msg_Name_1 := Symbol_Name1;
471                               Error_Msg ("unknown symbol %", Symbol_Pos1);
472                               Symbol_Value1 := No_String;
473                            end if;
474
475                         else
476                            Symbol_Value1 :=
477                              Mapping.Table (Symbol1).Value;
478                         end if;
479
480                         Symbol2 := Index_Of (Symbol_Name2);
481
482                         if Symbol2 = No_Symbol then
483                            if Undefined_Symbols_Are_False then
484                               Symbol_Value2 := String_False;
485
486                            else
487                               Error_Msg_Name_1 := Symbol_Name2;
488                               Error_Msg ("unknown symbol %", Symbol_Pos2);
489                               Symbol_Value2 := No_String;
490                            end if;
491
492                         else
493                            Symbol_Value2 := Mapping.Table (Symbol2).Value;
494                         end if;
495
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;
503
504                   elsif Token = Tok_String_Literal then
505
506                      --  symbol = "value"
507
508                      if Evaluation then
509                         Symbol1 := Index_Of (Symbol_Name1);
510
511                         if Symbol1 = No_Symbol then
512                            if Undefined_Symbols_Are_False then
513                               Symbol_Value1 := String_False;
514
515                            else
516                               Error_Msg_Name_1 := Symbol_Name1;
517                               Error_Msg ("unknown symbol %", Symbol_Pos1);
518                               Symbol_Value1 := No_String;
519                            end if;
520
521                         else
522                            Symbol_Value1 := Mapping.Table (Symbol1).Value;
523                         end if;
524
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;
532
533                      Scan.all;
534
535                   else
536                      Error_Msg
537                        ("symbol or literal string expected", Token_Ptr);
538                   end if;
539
540                else
541                   --  symbol (True or False)
542
543                   if Evaluation then
544                      Symbol1 := Index_Of (Symbol_Name1);
545
546                      if Symbol1 = No_Symbol then
547                         if Undefined_Symbols_Are_False then
548                            Symbol_Value1 := String_False;
549
550                         else
551                            Error_Msg_Name_1 := Symbol_Name1;
552                            Error_Msg ("unknown symbol %", Symbol_Pos1);
553                            Symbol_Value1 := No_String;
554                         end if;
555
556                      else
557                         Symbol_Value1 := Mapping.Table (Symbol1).Value;
558                      end if;
559
560                      if Symbol_Value1 /= No_String then
561                         String_To_Name_Buffer (Symbol_Value1);
562
563                         for Index in 1 .. Name_Len loop
564                            Name_Buffer (Index) :=
565                              Fold_Lower (Name_Buffer (Index));
566                         end loop;
567
568                         if Name_Buffer (1 .. Name_Len) = "true" then
569                            Current_Result := True;
570
571                         elsif Name_Buffer (1 .. Name_Len) = "false" then
572                            Current_Result := False;
573
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;
583
584             when others =>
585                Error_Msg ("`(`, NOT or symbol expected", Token_Ptr);
586          end case;
587
588          --  Update the cumulative final result
589
590          case Current_Operator is
591             when None =>
592                Final_Result := Current_Result;
593
594             when Op_Or =>
595                Final_Result := Final_Result or Current_Result;
596
597             when Op_And =>
598                Final_Result := Final_Result and Current_Result;
599          end case;
600
601          --  Check the next operator
602
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;
607
608             Current_Operator := Op_And;
609             Scan.all;
610
611             if Token = Tok_Then then
612                Scan.all;
613
614                if Final_Result = False then
615                   Evaluation := False;
616                end if;
617             end if;
618
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;
623
624             Current_Operator := Op_Or;
625             Scan.all;
626
627             if Token = Tok_Else then
628                Scan.all;
629
630                if Final_Result then
631                   Evaluation := False;
632                end if;
633             end if;
634
635          else
636             --  No operator: exit the term loop
637
638             exit;
639          end if;
640       end loop;
641
642       return Final_Result;
643    end Expression;
644
645    -----------------------
646    -- Go_To_End_Of_Line --
647    -----------------------
648
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
652
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;
659
660    --------------
661    -- Index_Of --
662    --------------
663
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;
673
674       return No_Symbol;
675    end Index_Of;
676
677    ----------------
678    -- Preprocess --
679    ----------------
680
681    procedure Preprocess is
682       Start_Of_Processing : Source_Ptr;
683       Cond : Boolean;
684       Preprocessor_Line : Boolean := False;
685
686       procedure Output (From, To : Source_Ptr);
687       --  Output the characters with indices From .. To in the buffer
688       --  to the output file.
689
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.
695
696       ------------
697       -- Output --
698       ------------
699
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;
706
707       -----------------
708       -- Output_Line --
709       -----------------
710
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;
716
717             elsif Comment_Deleted_Lines then
718                Put_Char ('-');
719                Put_Char ('-');
720                Put_Char ('!');
721
722                if From < To then
723                   Put_Char (' ');
724                   Output (From, To);
725                end if;
726
727                New_EOL.all;
728             end if;
729
730          else
731             Output (From, To);
732             New_EOL.all;
733          end if;
734       end Output_Line;
735
736    --  Start of processing for Preprocess
737
738    begin
739       Start_Of_Processing := Scan_Ptr;
740
741       --  We need to call Scan for the first time, because Initialize_Scanner
742       --  is no longer doing it.
743
744       Scan.all;
745
746       Input_Line_Loop : loop
747          exit Input_Line_Loop when Token = Tok_EOF;
748
749          Preprocessor_Line := False;
750
751          if Token /= Tok_End_Of_Line then
752
753             --  Preprocessor line
754
755             if Token = Tok_Special and then Special_Character = '#' then
756                   Preprocessor_Line := True;
757                   Scan.all;
758
759                   case Token is
760
761                      --  #if
762
763                      when Tok_If =>
764                         declare
765                            If_Ptr : constant Source_Ptr := Token_Ptr;
766
767                         begin
768                            Scan.all;
769                            Cond := Expression (not Deleting);
770
771                            --  Check for an eventual "then"
772
773                            if Token = Tok_Then then
774                               Scan.all;
775                            end if;
776
777                            --  It is an error to have trailing characters after
778                            --  the condition or "then".
779
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;
788
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.
794
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);
800
801                            begin
802                               Pp_States.Increment_Last;
803                               Pp_States.Table (Pp_States.Last) := New_State;
804                            end;
805                         end;
806
807                      --  #elsif
808
809                      when Tok_Elsif =>
810                         Cond := False;
811
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);
817
818                         else
819                            Cond :=
820                              not Pp_States.Table (Pp_States.Last).Match_Seen;
821                         end if;
822
823                         Scan.all;
824                         Cond := Expression (Cond);
825
826                         --  Check for an eventual "then"
827
828                         if Token = Tok_Then then
829                            Scan.all;
830                         end if;
831
832                         --  It is an error to have trailing characters after
833                         --  the condition or "then".
834
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);
841
842                            Go_To_End_Of_Line;
843                         end if;
844
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;
860
861                      --  #else
862
863                      when Tok_Else =>
864                         if Pp_States.Last = 0 then
865                            Error_Msg ("no IF for this ELSE", Token_Ptr);
866
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;
872
873                         --  Set the possibly new values of Deleting and
874                         --  Match_Seen.
875
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;
880
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;
887
888                            --  Set the Else_Ptr to check for illegal #elsif
889                            --  later.
890
891                            Pp_States.Table (Pp_States.Last).Else_Ptr :=
892                              Token_Ptr;
893                         end if;
894
895                         Scan.all;
896
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;
906
907                      --  #end if;
908
909                      when Tok_End =>
910                         if Pp_States.Last = 0 then
911                            Error_Msg ("no IF for this END", Token_Ptr);
912                         end if;
913
914                         Scan.all;
915
916                         if Token /= Tok_If then
917                            Error_Msg ("IF expected", Token_Ptr);
918
919                         else
920                            Scan.all;
921
922                            if Token /= Tok_Semicolon then
923                               Error_Msg ("`;` Expected", Token_Ptr);
924
925                            else
926                               Scan.all;
927
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;
939
940                         --  In case of one of the errors above, skip the tokens
941                         --  until the end of line is reached.
942
943                         Go_To_End_Of_Line;
944
945                         --  Decrement the depth of the #if stack
946
947                         if Pp_States.Last > 0 then
948                            Pp_States.Decrement_Last;
949                         end if;
950
951                      --  Illegal preprocessor line
952
953                      when others =>
954                         if Pp_States.Last = 0 then
955                            Error_Msg ("IF expected", Token_Ptr);
956
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);
962
963                         else
964                            Error_Msg ("IF or `END IF` expected", Token_Ptr);
965                         end if;
966
967                         --  Skip to the end of this illegal line
968
969                         Go_To_End_Of_Line;
970                   end case;
971
972             --  Not a preprocessor line
973
974             else
975                --  Do not report errors for those lines, even if there are
976                --  Ada parsing errors.
977
978                Set_Ignore_Errors (To => True);
979
980                if Deleting then
981                   Go_To_End_Of_Line;
982
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;
993
994                         begin
995                            Scan.all;
996                            Change_Reserved_Keyword_To_Symbol;
997
998                            if Token = Tok_Identifier
999                              and then Token_Ptr = Dollar_Ptr + 1
1000                            then
1001                               --  $symbol
1002
1003                               Symbol := Index_Of (Token_Name);
1004
1005                               --  If symbol exists, replace by its value
1006
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);
1012
1013                                  if Mapping.Table (Symbol).Is_A_String then
1014
1015                                     --  Value is an Ada string
1016
1017                                     Put_Char ('"');
1018
1019                                     for J in 1 .. Name_Len loop
1020                                        Put_Char (Name_Buffer (J));
1021
1022                                        if Name_Buffer (J) = '"' then
1023                                           Put_Char ('"');
1024                                        end if;
1025                                     end loop;
1026
1027                                     Put_Char ('"');
1028
1029                                  else
1030                                     --  Value is a sequence of characters, not
1031                                     --  an Ada string.
1032
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;
1041
1042                      Scan.all;
1043                   end loop;
1044                end if;
1045
1046                Set_Ignore_Errors (To => False);
1047             end if;
1048          end if;
1049
1050          pragma Assert (Token = Tok_End_Of_Line or Token = Tok_EOF);
1051
1052          --  At this point, the token is either end of line or EOF.
1053          --  The line to possibly output stops just before the token.
1054
1055          Output_Line (Start_Of_Processing, Token_Ptr - 1);
1056
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.
1061
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;
1074
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.
1077
1078          Set_Ignore_Errors (To => True);
1079          Scan.all;
1080          Set_Ignore_Errors (To => False);
1081       end loop Input_Line_Loop;
1082
1083       --  Report an error for any missing some "#end if;"
1084
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;
1089
1090    ----------------
1091    -- Initialize --
1092    ----------------
1093
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)
1100    is
1101    begin
1102       if not Already_Initialized then
1103          Start_String;
1104          Store_String_Chars ("True");
1105          True_Value.Value := End_String;
1106
1107          Start_String;
1108          Empty_String := End_String;
1109
1110          Name_Len := 7;
1111          Name_Buffer (1 .. Name_Len) := "defined";
1112          Name_Defined := Name_Find;
1113
1114          Start_String;
1115          Store_String_Chars ("False");
1116          String_False := End_String;
1117
1118          Already_Initialized := True;
1119       end if;
1120
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;
1127
1128    ------------------
1129    -- List_Symbols --
1130    ------------------
1131
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.
1137
1138       function Lt (Op1, Op2 : Natural) return Boolean;
1139       --  Comparison routine for sort call
1140
1141       procedure Move (From : Natural; To : Natural);
1142       --  Move routine for sort call
1143
1144       --------
1145       -- Lt --
1146       --------
1147
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);
1153
1154       begin
1155          return S1 < S2;
1156       end Lt;
1157
1158       ----------
1159       -- Move --
1160       ----------
1161
1162       procedure Move (From : Natural; To : Natural) is
1163       begin
1164          Order (To) := Order (From);
1165       end Move;
1166
1167       package Sort_Syms is new GNAT.Heap_Sort_G (Move, Lt);
1168
1169       Max_L : Natural;
1170       --  Maximum length of any symbol
1171
1172    --  Start of processing for List_Symbols_Case
1173
1174    begin
1175       if Symbol_Table.Last (Mapping) = 0 then
1176          return;
1177       end if;
1178
1179       if Foreword'Length > 0 then
1180          Write_Eol;
1181          Write_Line (Foreword);
1182
1183          for J in Foreword'Range loop
1184             Write_Char ('=');
1185          end loop;
1186       end if;
1187
1188       --  Initialize the order
1189
1190       for J in Order'Range loop
1191          Order (J) := Symbol_Id (J);
1192       end loop;
1193
1194       --  Sort alphabetically
1195
1196       Sort_Syms.Sort (Order'Last);
1197
1198       Max_L := 7;
1199
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;
1204
1205       Write_Eol;
1206       Write_Str ("Symbol");
1207
1208       for J in 1 .. Max_L - 5 loop
1209          Write_Char (' ');
1210       end loop;
1211
1212       Write_Line ("Value");
1213
1214       Write_Str ("------");
1215
1216       for J in 1 .. Max_L - 5 loop
1217          Write_Char (' ');
1218       end loop;
1219
1220       Write_Line ("------");
1221
1222       for J in 1 .. Order'Last loop
1223          declare
1224             Data : constant Symbol_Data := Mapping.Table (Order (J));
1225
1226          begin
1227             Get_Name_String (Data.Original);
1228             Write_Str (Name_Buffer (1 .. Name_Len));
1229
1230             for K in Name_Len .. Max_L loop
1231                Write_Char (' ');
1232             end loop;
1233
1234             String_To_Name_Buffer (Data.Value);
1235
1236             if Data.Is_A_String then
1237                Write_Char ('"');
1238
1239                for J in 1 .. Name_Len loop
1240                   Write_Char (Name_Buffer (J));
1241
1242                   if Name_Buffer (J) = '"' then
1243                      Write_Char ('"');
1244                   end if;
1245                end loop;
1246
1247                Write_Char ('"');
1248
1249             else
1250                Write_Str (Name_Buffer (1 .. Name_Len));
1251             end if;
1252          end;
1253
1254          Write_Eol;
1255       end loop;
1256
1257       Write_Eol;
1258    end List_Symbols;
1259
1260    ----------------------
1261    -- Matching_Strings --
1262    ----------------------
1263
1264    function Matching_Strings (S1, S2 : String_Id) return Boolean is
1265    begin
1266       String_To_Name_Buffer (S1);
1267
1268       for Index in 1 .. Name_Len loop
1269          Name_Buffer (Index) := Fold_Lower (Name_Buffer (Index));
1270       end loop;
1271
1272       declare
1273          String1 : constant String := Name_Buffer (1 .. Name_Len);
1274
1275       begin
1276          String_To_Name_Buffer (S2);
1277
1278          for Index in 1 .. Name_Len loop
1279             Name_Buffer (Index) := Fold_Lower (Name_Buffer (Index));
1280          end loop;
1281
1282          return String1 = Name_Buffer (1 .. Name_Len);
1283       end;
1284    end Matching_Strings;
1285
1286    --------------------
1287    -- Parse_Def_File --
1288    --------------------
1289
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;
1298
1299       use ASCII;
1300
1301    begin
1302       Def_Line_Loop :
1303       loop
1304          Scan.all;
1305
1306          exit Def_Line_Loop when Token = Tok_EOF;
1307
1308          if Token /= Tok_End_Of_Line then
1309             Change_Reserved_Keyword_To_Symbol;
1310
1311             if Token /= Tok_Identifier then
1312                Error_Msg ("identifier expected", Token_Ptr);
1313                goto Cleanup;
1314             end if;
1315
1316             Symbol_Name := Token_Name;
1317             Name_Len := 0;
1318
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;
1323
1324             Original_Name := Name_Find;
1325             Scan.all;
1326
1327             if Token /= Tok_Colon_Equal then
1328                Error_Msg ("`:=` expected", Token_Ptr);
1329                goto Cleanup;
1330             end if;
1331
1332             Scan.all;
1333
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);
1340
1341                Scan.all;
1342
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;
1347
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);
1354
1355             else
1356                Value_Start := Token_Ptr;
1357                Value_End   := Token_Ptr - 1;
1358                Scan_Ptr    := Token_Ptr;
1359
1360                Value_Chars_Loop :
1361                loop
1362                   Ch := Sinput.Source (Scan_Ptr);
1363
1364                   case Ch is
1365                      when '_' | '.' | '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' =>
1366                         Value_End := Scan_Ptr;
1367                         Scan_Ptr := Scan_Ptr + 1;
1368
1369                      when ' ' | HT | VT | CR | LF | FF =>
1370                         exit Value_Chars_Loop;
1371
1372                      when others =>
1373                         Error_Msg ("illegal character", Scan_Ptr);
1374                         goto Cleanup;
1375                   end case;
1376                end loop Value_Chars_Loop;
1377
1378                Scan.all;
1379
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;
1384
1385                Start_String;
1386
1387                while Value_Start <= Value_End loop
1388                   Store_String_Char (Sinput.Source (Value_Start));
1389                   Value_Start := Value_Start + 1;
1390                end loop;
1391
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;
1398
1399             --  Now that we have the value, get the symbol index
1400
1401             Symbol := Index_Of (Symbol_Name);
1402
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.
1407
1408                if Mapping.Table (Symbol).On_The_Command_Line then
1409                   goto Continue;
1410                end if;
1411
1412             else
1413                --  As it is the first time we see this symbol, create a new
1414                --  entry in the table.
1415
1416                if Mapping.Table = null then
1417                   Symbol_Table.Init (Mapping);
1418                end if;
1419
1420                Symbol_Table.Increment_Last (Mapping);
1421                Symbol := Symbol_Table.Last (Mapping);
1422             end if;
1423
1424             Mapping.Table (Symbol) := Data;
1425             goto Continue;
1426
1427             <<Cleanup>>
1428                Set_Ignore_Errors (To => True);
1429
1430                while Token /= Tok_End_Of_Line and Token /= Tok_EOF loop
1431                   Scan.all;
1432                end loop;
1433
1434                Set_Ignore_Errors (To => False);
1435
1436             <<Continue>>
1437                null;
1438          end if;
1439       end loop Def_Line_Loop;
1440    end Parse_Def_File;
1441
1442 end Prep;