* config/rs6000/rs6000.md: Document why a pattern is not
[official-gcc.git] / gcc / ada / xgnatugn.adb
blob5a992f4e8ed728954d1e943f39109c1f8db6a82d
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT SYSTEM UTILITIES --
4 -- --
5 -- X G N A T U G N --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2003-2004 Free Software Foundation, Inc. --
10 -- --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 2, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
20 -- MA 02111-1307, USA. --
21 -- --
22 ------------------------------------------------------------------------------
24 -- This utility is used to process the source of gnat_ugn.texi to make a
25 -- version suitable for running through standard Texinfo processor. It is
26 -- invoked as follows:
28 -- xgnatugn <target> <in-file> <word-list> [ <out-file> [ <warnings> ] ]
30 -- 1. <target> is the target type of the manual, which is one of:
32 -- unw Unix and Windows platforms
33 -- vms OpenVMS
35 -- 2. <in-file> is the file name of the Texinfo file to be
36 -- preprocessed.
38 -- 3. <word-list> is the name of the word list file. This file is used for
39 -- rewriting the VMS edition. Each line contains a word mapping: The source
40 -- word in the first column, the target word in the second column. The
41 -- columns are separated by a '^' character. When preprocessing for VMS, the
42 -- first word is replaced with the second. (Words consist of letters,
43 -- digits, and the four characters "?-_~". A sequence of multiple words can
44 -- be replaced if they are listed in the first column, separated by a single
45 -- space character. If multiple words are to be replaced, there must be a
46 -- replacement for each prefix.)
48 -- 4. <out-file> (optional) is the name of the output file. It defaults to
49 -- gnat_ugn_unw.texi or gnat_ugn_vms.texi, depending on the target.
51 -- 5. <warnings> (optional, and allowed only if <out-file> is explicit)
52 -- can be any string. If present, it indicates that warning messages are
53 -- to be output to Standard_Error. If absent, no warning messages are
54 -- generated.
56 -- The following steps are performed:
58 -- In VMS mode
60 -- Any occurrences of ^alpha^beta^ are replaced by beta. The sequence
61 -- must fit on a single line, and there can only be one occurrence on a
62 -- line.
64 -- Any occurrences of a word in the Ug_Words list are replaced by the
65 -- appropriate vms equivalents. Note that replacements do not occur
66 -- within ^alpha^beta^ sequences.
68 -- Any occurence of [filename].extension, where extension one of the
69 -- following:
71 -- "o", "ads", "adb", "ali", "ada", "atb", "ats", "adc", "c"
73 -- replaced by the appropriate VMS names (all upper case with .o
74 -- replaced .OBJ). Note that replacements do not occur within
75 -- ^alpha^beta^ sequences.
77 -- In UNW mode
79 -- Any occurrences of ^alpha^beta^ are replaced by alpha. The sequence
80 -- must fit on a single line.
82 -- In both modes
84 -- The sequence ^^^ is replaced by a single ^. This escape sequence
85 -- must be used if the literal character ^ is to appear in the
86 -- output. A line containing this escape sequence may not also contain
87 -- a ^alpha^beta^ sequence.
89 -- Recognize @ifset and @ifclear (this is because we have menu problems
90 -- if we let makeinfo handle the ifset/ifclear pairs
92 with Ada.Command_Line; use Ada.Command_Line;
93 with Ada.Strings; use Ada.Strings;
94 with Ada.Strings.Fixed; use Ada.Strings.Fixed;
95 with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
96 with Ada.Strings.Maps; use Ada.Strings.Maps;
97 with Ada.Strings.Maps.Constants; use Ada.Strings.Maps.Constants;
98 with Ada.Text_IO; use Ada.Text_IO;
100 with GNAT.Spitbol; use GNAT.Spitbol;
101 with GNAT.Spitbol.Table_VString; use GNAT.Spitbol.Table_VString;
103 procedure Xgnatugn is
105 procedure Usage;
106 -- Print usage information. Invoked if an invalid command line is
107 -- encountered.
109 Output_File : File_Type;
110 -- The preprocessed output is written to this file
112 type Input_File is record
113 Name : VString;
114 Data : File_Type;
115 Line : Natural := 0;
116 end record;
117 -- Records information on an input file. Name and Line are used
118 -- in error messages, Line is updated automatically by Get_Line.
120 function Get_Line (Input : access Input_File) return String;
121 -- Returns a line from Input and performs the necessary
122 -- line-oriented checks (length, character set, trailing spaces).
124 Number_Of_Warnings : Natural := 0;
125 Number_Of_Errors : Natural := 0;
126 Warnings_Enabled : Boolean;
128 procedure Error
129 (Input : Input_File;
130 At_Character : Natural;
131 Message : String);
132 procedure Error
133 (Input : Input_File;
134 Message : String);
135 -- Prints a message reporting an error on line Input.Line. If
136 -- At_Character is not 0, indicate the exact character at which
137 -- the error occurs.
139 procedure Warning
140 (Input : Input_File;
141 At_Character : Natural;
142 Message : String);
143 procedure Warning
144 (Input : Input_File;
145 Message : String);
146 -- Like Error, but just print a warning message.
148 Dictionary_File : aliased Input_File;
149 procedure Read_Dictionary_File;
150 -- Dictionary_File is opened using the name given on the command
151 -- line. It contains the replacements for the Ug_Words list.
152 -- Read_Dictionary_File reads Dictionary_File and fills the
153 -- Ug_Words table.
155 Source_File : aliased Input_File;
156 procedure Process_Source_File;
157 -- Source_File is opened using the name given on the command line.
158 -- It contains the Texinfo source code. Process_Source_File
159 -- performs the necessary replacements.
161 type Target_Type is (UNW, VMS);
162 Target : Target_Type;
163 -- The target for which preprocessing is performed:
164 -- UNW (Unix and Windows) or VMS
165 -- The Target variable is initialized using the command line.
167 Valid_Characters : constant Character_Set
168 := To_Set (Span => (' ', '~'));
169 -- This array controls which characters are permitted in the input
170 -- file (after line breaks have been removed). Valid characters
171 -- are all printable ASCII characters and the space character.
173 Word_Characters : constant Character_Set :=
174 (To_Set (Ranges =>
175 (('0', '9'), ('a', 'z'), ('A', 'Z')))
176 or To_Set ("?-_~"));
177 -- The characters which are permitted in words. Other (valid)
178 -- characters are assumed to be delimiters between words. Note that
179 -- this set has to include all characters of the source words of the
180 -- Ug_Words dictionary.
182 Reject_Trailing_Spaces : constant Boolean := True;
183 -- Controls whether Xgnatug rejects superfluous space characters
184 -- at the end of lines.
186 Maximum_Line_Length : constant Positive := 79;
187 Fatal_Line_Length_Limit : constant Positive := 5000;
188 Fatal_Line_Length : exception;
189 -- If Maximum_Line_Length is exceeded in an input file, an error
190 -- message is printed. If Fatal_Line_Length is exceeded,
191 -- execution terminates with a Fatal_Line_Length exception.
193 VMS_Escape_Character : constant Character := '^';
194 -- The character used to mark VMS alternatives (^alpha^beta^).
196 Extensions : GNAT.Spitbol.Table_VString.Table (20);
197 procedure Initialize_Extensions;
198 -- This table records extensions and their replacement for
199 -- rewriting filenames in the VMS version of the manual.
201 function Is_Extension (Extension : String) return Boolean;
202 function Get_Replacement_Extension (Extension : String) return String;
203 -- These functions query the replacement table. Is_Extension
204 -- checks if the given string is a known extension.
205 -- Get_Replacement returns the replacement extension.
207 Ug_Words : GNAT.Spitbol.Table_VString.Table (200);
208 function Is_Known_Word (Word : String) return Boolean;
209 function Get_Replacement_Word (Word : String) return String;
210 -- The Ug_Words table lists replacement words for the VMS version
211 -- of the manual. Is_Known_Word and Get_Replacement_Word query
212 -- this table. The table is filled using Read_Dictionary_File.
214 function Rewrite_Source_Line (Line : String) return String;
215 -- This subprogram takes a line and rewrites it according to Target.
216 -- It relies on information in Source_File to generate error messages.
218 type Conditional is (Set, Clear);
219 procedure Push_Conditional (Cond : Conditional; Flag : Target_Type);
220 procedure Pop_Conditional (Cond : Conditional);
221 -- These subprograms deal with conditional processing (@ifset/@ifclear).
222 -- They rely on information in Source_File to generate error messages.
224 function Currently_Excluding return Boolean;
225 -- Returns true if conditional processing directives imply that the
226 -- current line should not be included in the output.
228 function VMS_Context_Determined return Boolean;
229 -- Returns true if, in the current conditional preprocessing context, we
230 -- always have a VMS or a non-VMS version, regardless of the value of
231 -- Target.
233 function In_VMS_Section return Boolean;
234 -- Returns True if in an "@ifset vms" section.
236 procedure Check_No_Pending_Conditional;
237 -- Checks that all preprocessing directives have been properly matched by
238 -- their @end counterpart. If this is not the case, print an error
239 -- message.
241 -- The following definitions implement a stack to track the conditional
242 -- preprocessing context.
244 type Conditional_Context is record
245 Starting_Line : Positive;
246 Cond : Conditional;
247 Flag : Target_Type;
248 Excluding : Boolean;
249 end record;
251 Conditional_Stack_Depth : constant := 3;
253 Conditional_Stack :
254 array (1 .. Conditional_Stack_Depth) of Conditional_Context;
256 Conditional_TOS : Natural := 0;
257 -- Pointer to the Top Of Stack for Conditional_Stack.
259 -----------
260 -- Usage --
261 -----------
263 procedure Usage is
264 begin
265 Put_Line (Standard_Error,
266 "usage: xgnatug TARGET SOURCE DICTIONARY [OUTFILE [WARNINGS]]");
267 New_Line;
268 Put_Line (Standard_Error, "TARGET is one of:");
270 for T in Target_Type'Range loop
271 Put_Line (Standard_Error, " " & Target_Type'Image (T));
272 end loop;
274 New_Line;
275 Put_Line (Standard_Error, "SOURCE is the source file to process.");
276 New_Line;
277 Put_Line (Standard_Error, "DICTIONARY is the name of a file "
278 & "that contains word replacements");
279 Put_Line (Standard_Error, "for the VMS version.");
280 New_Line;
281 Put_Line (Standard_Error,
282 "OUT-FILE, if present, is the output file to be created;");
283 Put_Line (Standard_Error,
284 "If OUT-FILE is absent, the output file is either " &
285 "gnat_ugn_unw.texi, ");
286 Put_Line (Standard_Error,
287 "or gnat_ugn_vms.texi, depending on TARGET.");
288 New_Line;
289 Put_Line (Standard_Error,
290 "WARNINGS, if present, is any string;");
291 Put_Line (Standard_Error,
292 "it will result in warning messages (e.g., line too long))");
293 Put_Line (Standard_Error,
294 "being output to Standard_Error.");
295 end Usage;
297 --------------
298 -- Get_Line --
299 --------------
301 function Get_Line (Input : access Input_File) return String is
302 Line_Buffer : String (1 .. Fatal_Line_Length_Limit);
303 Last : Natural;
305 begin
306 Input.Line := Input.Line + 1;
307 Get_Line (Input.Data, Line_Buffer, Last);
309 if Last = Line_Buffer'Last then
310 Error (Input.all, "line exceeds fatal line length limit");
311 raise Fatal_Line_Length;
312 end if;
314 declare
315 Line : String renames Line_Buffer (Line_Buffer'First .. Last);
317 begin
318 for J in Line'Range loop
319 if not Is_In (Line (J), Valid_Characters) then
320 Error (Input.all, J, "invalid character");
321 exit;
322 end if;
323 end loop;
325 if Line'Length > Maximum_Line_Length then
326 Warning (Input.all, Maximum_Line_Length + 1, "line too long");
327 end if;
329 if Reject_Trailing_Spaces
330 and then Line'Length > 0
331 and then Line (Line'Last) = ' '
332 then
333 Error (Input.all, Line'Last, "trailing space character");
334 end if;
336 return Trim (Line, Right);
337 end;
338 end Get_Line;
340 -----------
341 -- Error --
342 -----------
344 procedure Error
345 (Input : Input_File;
346 Message : String)
348 begin
349 Error (Input, 0, Message);
350 end Error;
352 procedure Error
353 (Input : Input_File;
354 At_Character : Natural;
355 Message : String)
357 Line_Image : constant String := Integer'Image (Input.Line);
358 At_Character_Image : constant String := Integer'Image (At_Character);
359 -- These variables are required because we have to drop the leading
360 -- space character.
362 begin
363 Number_Of_Errors := Number_Of_Errors + 1;
365 if At_Character > 0 then
366 Put_Line (Standard_Error,
367 S (Input.Name) & ':'
368 & Line_Image (Line_Image'First + 1 .. Line_Image'Last) & ':'
369 & At_Character_Image (At_Character_Image'First + 1
370 .. At_Character_Image'Last)
371 & ": "
372 & Message);
373 else
374 Put_Line (Standard_Error,
375 S (Input.Name) & ':'
376 & Line_Image (Line_Image'First + 1 .. Line_Image'Last)
377 & ": "
378 & Message);
379 end if;
380 end Error;
382 -------------
383 -- Warning --
384 -------------
386 procedure Warning
387 (Input : Input_File;
388 Message : String)
390 begin
391 if Warnings_Enabled then
392 Warning (Input, 0, Message);
393 end if;
394 end Warning;
396 procedure Warning
397 (Input : Input_File;
398 At_Character : Natural;
399 Message : String)
401 Line_Image : constant String := Integer'Image (Input.Line);
402 At_Character_Image : constant String := Integer'Image (At_Character);
403 -- These variables are required because we have to drop the leading
404 -- space character.
406 begin
407 if not Warnings_Enabled then
408 return;
409 end if;
411 Number_Of_Warnings := Number_Of_Warnings + 1;
413 if At_Character > 0 then
414 Put_Line (Standard_Error,
415 S (Input.Name) & ':'
416 & Line_Image (Line_Image'First + 1 .. Line_Image'Last) & ':'
417 & At_Character_Image (At_Character_Image'First + 1
418 .. At_Character_Image'Last)
419 & ": warning: "
420 & Message);
421 else
422 Put_Line (Standard_Error,
423 S (Input.Name) & ':'
424 & Line_Image (Line_Image'First + 1 .. Line_Image'Last)
425 & ": warning: "
426 & Message);
427 end if;
428 end Warning;
430 --------------------------
431 -- Read_Dictionary_File --
432 --------------------------
434 procedure Read_Dictionary_File is
435 begin
436 while not End_Of_File (Dictionary_File.Data) loop
437 declare
438 Line : constant String :=
439 Get_Line (Dictionary_File'Access);
440 Split : constant Natural :=
441 Index (Line, (1 => VMS_Escape_Character));
443 begin
444 if Line'Length = 0 then
445 Error (Dictionary_File, "empty line in dictionary file");
447 elsif Line (Line'First) = ' ' then
448 Error (Dictionary_File, 1, "line starts with space character");
450 elsif Split = 0 then
451 Error (Dictionary_File, "line does not contain "
452 & VMS_Escape_Character & " character");
453 else
454 declare
455 Source : constant String :=
456 Trim (Line (1 .. Split - 1), Both);
457 Target : constant String :=
458 Trim (Line (Split + 1 .. Line'Last), Both);
459 Two_Spaces : constant Natural :=
460 Index (Source, " ");
461 Non_Word_Character : constant Natural :=
462 Index (Source,
463 Word_Characters or
464 To_Set (" "),
465 Outside);
467 begin
468 if Two_Spaces /= 0 then
469 Error (Dictionary_File, Two_Spaces,
470 "multiple space characters in source word");
471 end if;
473 if Non_Word_Character /= 0 then
474 Error (Dictionary_File, Non_Word_Character,
475 "illegal character in source word");
476 end if;
478 if Source'Length = 0 then
479 Error (Dictionary_File, "source is empty");
481 elsif Target'Length = 0 then
482 Error (Dictionary_File, "target is empty");
484 else
485 Set (Ug_Words, Source, V (Target));
487 -- Ensure that if Source is a sequence of words
488 -- "WORD1 WORD2 ...", we already have a mapping for
489 -- "WORD1".
491 for J in Source'Range loop
492 if Source (J) = ' ' then
493 declare
494 Prefix : String renames
495 Source (Source'First .. J - 1);
497 begin
498 if not Is_Known_Word (Prefix) then
499 Error (Dictionary_File,
500 "prefix '" & Prefix
501 & "' not known at this point");
502 end if;
503 end;
504 end if;
505 end loop;
506 end if;
507 end;
508 end if;
509 end;
510 end loop;
511 end Read_Dictionary_File;
513 -------------------------
514 -- Rewrite_Source_Line --
515 -------------------------
517 function Rewrite_Source_Line (Line : String) return String is
519 -- We use a simple lexer to split the line into tokens:
521 -- Word consisting entirely of Word_Characters
522 -- VMS_Alternative ^alpha^beta^ replacement (but not ^^^)
523 -- Space a space character
524 -- Other everything else (sequence of non-word characters)
525 -- VMS_Error incomplete VMS alternative
526 -- End_Of_Line no more characters on this line
528 -- A sequence of three VMS_Escape_Characters is automatically
529 -- collapsed to an Other token.
531 type Token_Span is record
532 First, Last : Positive;
533 end record;
534 -- The character range covered by a token in Line
536 type Token_Kind is (End_Of_Line, Word, Other,
537 VMS_Alternative, VMS_Error);
538 type Token_Record (Kind : Token_Kind := End_Of_Line) is record
539 First : Positive;
540 case Kind is
541 when Word | Other =>
542 Span : Token_Span;
543 when VMS_Alternative =>
544 Non_VMS, VMS : Token_Span;
545 when VMS_Error | End_Of_Line =>
546 null;
547 end case;
548 end record;
550 Input_Position : Positive := Line'First;
551 Token : Token_Record;
552 -- The position of the next character to be processed by Next_Token
554 procedure Next_Token;
555 -- Returns the next token in Line, starting at Input_Position
557 Rewritten_Line : VString;
558 -- Collects the line as it is rewritten
560 procedure Rewrite_Word;
561 -- The current token is assumed to be a Word. When processing the VMS
562 -- version of the manual, additional tokens are gathered to check if
563 -- we have a file name or a sequence of known words.
565 procedure Maybe_Rewrite_Extension;
566 -- The current token is assumed to be Other. When processing the VMS
567 -- version of the manual and the token represents a single dot ".",
568 -- the following word is rewritten according to the rules for
569 -- extensions.
571 VMS_Token_Seen : Boolean := False;
572 -- This is set to true if a VMS_Alternative has been encountered, or a
573 -- ^^^ token.
575 ----------------
576 -- Next_Token --
577 ----------------
579 procedure Next_Token is
580 Remaining_Line : String renames Line (Input_Position .. Line'Last);
581 Last_Character : Natural;
583 begin
584 if Remaining_Line'Length = 0 then
585 Token := (End_Of_Line, Remaining_Line'First);
586 return;
587 end if;
589 -- ^alpha^beta^, the VMS_Alternative case.
591 if Remaining_Line (Remaining_Line'First) = VMS_Escape_Character then
592 declare
593 VMS_Second_Character, VMS_Third_Character : Natural;
595 begin
596 if VMS_Token_Seen then
597 Error (Source_File, Remaining_Line'First,
598 "multiple " & VMS_Escape_Character
599 & " characters on a single line");
600 else
601 VMS_Token_Seen := True;
602 end if;
604 -- Find the second and third escape character. If one of
605 -- them is not present, generate an error token.
607 VMS_Second_Character :=
608 Index (Remaining_Line (Remaining_Line'First + 1
609 .. Remaining_Line'Last),
610 (1 => VMS_Escape_Character));
612 if VMS_Second_Character = 0 then
613 Input_Position := Remaining_Line'Last + 1;
614 Token := (VMS_Error, Remaining_Line'First);
615 return;
616 end if;
618 VMS_Third_Character :=
619 Index (Remaining_Line (VMS_Second_Character + 1
620 .. Remaining_Line'Last),
621 (1 => VMS_Escape_Character));
623 if VMS_Third_Character = 0 then
624 Input_Position := Remaining_Line'Last + 1;
625 Token := (VMS_Error, Remaining_Line'First);
626 return;
627 end if;
629 -- Consume all the characters we are about to include in
630 -- the token.
632 Input_Position := VMS_Third_Character + 1;
634 -- Check if we are in a ^^^ situation, and return an Other
635 -- token in this case.
637 if Remaining_Line'First + 1 = VMS_Second_Character
638 and then Remaining_Line'First + 2 = VMS_Third_Character
639 then
640 Token := (Other, Remaining_Line'First,
641 (Remaining_Line'First, Remaining_Line'First));
642 return;
643 end if;
645 Token := (VMS_Alternative, Remaining_Line'First,
646 (Remaining_Line'First + 1, VMS_Second_Character - 1),
647 (VMS_Second_Character + 1, VMS_Third_Character - 1));
648 return;
649 end;
650 end if; -- VMS_Alternative
652 -- The Word case. Search for characters not in Word_Characters.
653 -- We have found a word if the first non-word character is not
654 -- the first character in Remaining_Line, i.e. if Remaining_Line
655 -- starts with a word character.
657 Last_Character := Index (Remaining_Line, Word_Characters, Outside);
658 if Last_Character /= Remaining_Line'First then
660 -- If we haven't found a character which is not in
661 -- Word_Characters, all remaining characters are part of the
662 -- current Word token.
664 if Last_Character = 0 then
665 Last_Character := Remaining_Line'Last + 1;
666 end if;
668 Input_Position := Last_Character;
669 Token := (Word, Remaining_Line'First,
670 (Remaining_Line'First, Last_Character - 1));
671 return;
672 end if;
674 -- Remaining characters are in the Other category. To speed
675 -- up processing, we collect them together if there are several
676 -- of them.
678 Input_Position := Last_Character + 1;
679 Token := (Other,
680 Remaining_Line'First,
681 (Remaining_Line'First, Last_Character));
682 end Next_Token;
684 ------------------
685 -- Rewrite_Word --
686 ------------------
688 procedure Rewrite_Word is
689 First_Word : String
690 renames Line (Token.Span.First .. Token.Span.Last);
692 begin
693 -- We do not perform any error checking below, so we can just skip
694 -- all processing for the non-VMS version.
696 if Target /= VMS then
697 Append (Rewritten_Line, First_Word);
698 Next_Token;
699 return;
700 end if;
702 if Is_Known_Word (First_Word) then
704 -- If we have a word from the dictionary, we look for the
705 -- longest possible sequence we can rewrite.
707 declare
708 Seq : Token_Span := Token.Span;
709 Lost_Space : Boolean := False;
711 begin
712 Next_Token;
713 loop
714 if Token.Kind = Other
715 and then Line (Token.Span.First .. Token.Span.Last) = " "
716 then
717 Next_Token;
718 if Token.Kind /= Word
719 or else not Is_Known_Word (Line (Seq.First
720 .. Token.Span.Last))
721 then
722 -- When we reach this point, the following
723 -- conditions are true:
725 -- Seq is a known word.
726 -- The previous token was a space character.
727 -- Seq extended to the current token is not a
728 -- known word.
730 Lost_Space := True;
731 exit;
733 else
735 -- Extend Seq to cover the current (known) word.
737 Seq.Last := Token.Span.Last;
738 Next_Token;
739 end if;
741 else
742 -- When we reach this point, the following conditions
743 -- are true:
745 -- Seq is a known word.
746 -- The previous token was a word.
747 -- The current token is not a space character.
749 exit;
750 end if;
751 end loop;
753 -- Rewrite Seq, and add the lost space if necessary
755 Append (Rewritten_Line,
756 Get_Replacement_Word (Line (Seq.First .. Seq.Last)));
757 if Lost_Space then
758 Append (Rewritten_Line, ' ');
759 end if;
761 -- The unknown token will be processed during the
762 -- next iteration of the main loop.
763 return;
764 end;
765 end if;
767 Next_Token;
769 if Token.Kind = Other
770 and then Line (Token.Span.First .. Token.Span.Last) = "."
771 then
772 -- Deal with extensions
774 Next_Token;
775 if Token.Kind = Word
776 and then Is_Extension (Line (Token.Span.First
777 .. Token.Span.Last))
778 then
779 -- We have discovered a file extension. Convert the file
780 -- name to upper case.
782 Append (Rewritten_Line,
783 Translate (First_Word, Upper_Case_Map) & '.');
784 Append (Rewritten_Line,
785 Get_Replacement_Extension
786 (Line (Token.Span.First .. Token.Span.Last)));
787 Next_Token;
788 else
789 -- We already have: Word ".", followed by an unknown
790 -- token.
792 Append (Rewritten_Line, First_Word & '.');
794 -- The unknown token will be processed during the next
795 -- iteration of the main loop.
796 end if;
798 else
799 -- We have an unknown Word, followed by an unknown token.
800 -- The unknown token will be processed by the outer loop.
802 Append (Rewritten_Line, First_Word);
803 end if;
804 end Rewrite_Word;
806 -----------------------------
807 -- Maybe_Rewrite_Extension --
808 -----------------------------
810 procedure Maybe_Rewrite_Extension is
811 begin
812 -- Again, we need no special processing in the non-VMS case
814 if Target = VMS
815 and then Line (Token.Span.First .. Token.Span.Last) = "."
816 then
817 -- This extension is not preceded by a word, otherwise
818 -- Rewrite_Word would have handled it.
820 Next_Token;
821 if Token.Kind = Word
822 and then Is_Extension (Line (Token.Span.First
823 .. Token.Span.Last))
824 then
825 Append (Rewritten_Line, '.' & Get_Replacement_Extension
826 (Line (Token.Span.First .. Token.Span.Last)));
827 Next_Token;
828 else
829 Append (Rewritten_Line, '.');
830 end if;
831 else
832 Append (Rewritten_Line, Line (Token.Span.First
833 .. Token.Span.Last));
834 Next_Token;
835 end if;
836 end Maybe_Rewrite_Extension;
838 -- Start of processing for Process_Source_Line
840 begin
841 -- The following parser recognizes the following special token
842 -- sequences:
844 -- Word "." Word rewrite as file name if second word is extension
845 -- Word " " Word rewrite as a single word using Ug_Words table
847 Next_Token;
848 loop
849 case Token.Kind is
850 when End_Of_Line =>
851 exit;
853 when Word =>
854 Rewrite_Word;
856 when Other =>
857 Maybe_Rewrite_Extension;
859 when VMS_Alternative =>
860 if VMS_Context_Determined then
861 if (not In_VMS_Section)
862 or else
863 Line (Token.VMS.First .. Token.VMS.Last) /=
864 Line (Token.Non_VMS.First .. Token.Non_VMS.Last)
865 then
866 Warning (Source_File, Token.First,
867 "VMS alternative already determined "
868 & "by conditionals");
869 end if;
870 end if;
871 if Target = VMS then
872 Append (Rewritten_Line, Line (Token.VMS.First
873 .. Token.VMS.Last));
874 else
875 Append (Rewritten_Line, Line (Token.Non_VMS.First
876 .. Token.Non_VMS.Last));
877 end if;
878 Next_Token;
880 when VMS_Error =>
881 Error (Source_File, Token.First, "invalid VMS alternative");
882 Next_Token;
883 end case;
884 end loop;
886 return S (Rewritten_Line);
887 end Rewrite_Source_Line;
889 -------------------------
890 -- Process_Source_File --
891 -------------------------
893 procedure Process_Source_File is
894 Ifset : constant String := "@ifset ";
895 Ifclear : constant String := "@ifclear ";
896 Endsetclear : constant String := "@end ";
897 -- Strings to be recognized for conditional processing.
899 begin
900 while not End_Of_File (Source_File.Data) loop
901 declare
902 Line : constant String := Get_Line (Source_File'Access);
903 Rewritten : constant String := Rewrite_Source_Line (Line);
904 -- We unconditionally rewrite the line so that we can check the
905 -- syntax of all lines, and not only those which are actually
906 -- included in the output.
908 Have_Conditional : Boolean := False;
909 -- True if we have encountered a conditional preprocessing
910 -- directive.
912 Cond : Conditional;
913 -- The kind of the directive.
915 Flag : Target_Type;
916 -- Its flag.
918 begin
919 -- If the line starts with @ifset or @ifclear, we try to convert
920 -- the following flag to one of our target types. If we fail,
921 -- Have_Conditional remains False.
923 if Line'Length >= Ifset'Length
924 and then Line (1 .. Ifset'Length) = Ifset
925 then
926 Cond := Set;
928 declare
929 Arg : constant String :=
930 Trim (Line (Ifset'Length + 1 .. Line'Last), Both);
932 begin
933 Flag := Target_Type'Value (Arg);
935 if Translate (Target_Type'Image (Flag), Lower_Case_Map)
936 /= Arg
937 then
938 Error (Source_File, "flag has to be lowercase");
939 end if;
941 Have_Conditional := True;
943 exception
944 when Constraint_Error =>
945 Error (Source_File, "unknown flag for '@ifset'");
946 end;
948 elsif Line'Length >= Ifclear'Length
949 and then Line (1 .. Ifclear'Length) = Ifclear
950 then
951 Cond := Clear;
953 declare
954 Arg : constant String :=
955 Trim (Line (Ifclear'Length + 1 .. Line'Last), Both);
957 begin
958 Flag := Target_Type'Value (Arg);
959 if Translate (Target_Type'Image (Flag), Lower_Case_Map)
960 /= Arg
961 then
962 Error (Source_File, "flag has to be lowercase");
963 end if;
965 Have_Conditional := True;
967 exception
968 when Constraint_Error =>
969 Error (Source_File, "unknown flag for '@ifclear'");
970 end;
971 end if;
973 if Have_Conditional then
975 -- We create a new conditional context and suppress the
976 -- directive in the output.
978 Push_Conditional (Cond, Flag);
980 elsif Line'Length >= Endsetclear'Length
981 and then Line (1 .. Endsetclear'Length) = Endsetclear
982 then
983 -- The '@end ifset'/'@end ifclear' case is handled here. We
984 -- have to pop the conditional context.
986 declare
987 First, Last : Natural;
989 begin
990 Find_Token (Source => Line (Endsetclear'Length + 1
991 .. Line'Length),
992 Set => Letter_Set,
993 Test => Inside,
994 First => First,
995 Last => Last);
997 if Last = 0 then
998 Error (Source_File, "'@end' without argument");
999 else
1000 if Line (First .. Last) = "ifset" then
1001 Have_Conditional := True;
1002 Cond := Set;
1003 elsif Line (First .. Last) = "ifclear" then
1004 Have_Conditional := True;
1005 Cond := Clear;
1006 end if;
1008 if Have_Conditional then
1009 Pop_Conditional (Cond);
1010 end if;
1012 -- We fall through to the ordinary case for other @end
1013 -- directives.
1015 end if; -- @end without argument
1016 end;
1017 end if; -- Have_Conditional
1019 if not Have_Conditional then
1021 -- The ordinary case.
1023 if not Currently_Excluding then
1024 Put_Line (Output_File, Rewritten);
1025 end if;
1026 end if;
1027 end;
1028 end loop;
1030 Check_No_Pending_Conditional;
1031 end Process_Source_File;
1033 ---------------------------
1034 -- Initialize_Extensions --
1035 ---------------------------
1037 procedure Initialize_Extensions is
1039 procedure Add (Extension : String);
1040 -- Adds an extension which is replaced with itself (in upper
1041 -- case).
1043 procedure Add (Extension, Replacement : String);
1044 -- Adds an extension with a custom replacement.
1046 ---------
1047 -- Add --
1048 ---------
1050 procedure Add (Extension : String) is
1051 begin
1052 Add (Extension, Translate (Extension, Upper_Case_Map));
1053 end Add;
1055 procedure Add (Extension, Replacement : String) is
1056 begin
1057 Set (Extensions, Extension, V (Replacement));
1058 end Add;
1060 -- Start of processing for Initialize_Extensions
1062 begin
1063 -- To avoid performance degradation, increase the constant in the
1064 -- definition of Extensions above if you add more extensions here.
1066 Add ("o", "OBJ");
1067 Add ("ads");
1068 Add ("adb");
1069 Add ("ali");
1070 Add ("ada");
1071 Add ("atb");
1072 Add ("ats");
1073 Add ("adc");
1074 Add ("c");
1075 end Initialize_Extensions;
1077 ------------------
1078 -- Is_Extension --
1079 ------------------
1081 function Is_Extension (Extension : String) return Boolean is
1082 begin
1083 return Present (Extensions, Extension);
1084 end Is_Extension;
1086 -------------------------------
1087 -- Get_Replacement_Extension --
1088 -------------------------------
1090 function Get_Replacement_Extension (Extension : String) return String is
1091 begin
1092 return S (Get (Extensions, Extension));
1093 end Get_Replacement_Extension;
1095 -------------------
1096 -- Is_Known_Word --
1097 -------------------
1099 function Is_Known_Word (Word : String) return Boolean is
1100 begin
1101 return Present (Ug_Words, Word);
1102 end Is_Known_Word;
1104 --------------------------
1105 -- Get_Replacement_Word --
1106 --------------------------
1108 function Get_Replacement_Word (Word : String) return String is
1109 begin
1110 return S (Get (Ug_Words, Word));
1111 end Get_Replacement_Word;
1113 ----------------------
1114 -- Push_Conditional --
1115 ----------------------
1117 procedure Push_Conditional (Cond : Conditional; Flag : Target_Type) is
1118 Will_Exclude : Boolean;
1120 begin
1121 -- If we are already in an excluding context, inherit this property,
1122 -- otherwise calculate it from scratch.
1124 if Conditional_TOS > 0
1125 and then Conditional_Stack (Conditional_TOS).Excluding
1126 then
1127 Will_Exclude := True;
1128 else
1129 case Cond is
1130 when Set =>
1131 Will_Exclude := Flag /= Target;
1132 when Clear =>
1133 Will_Exclude := Flag = Target;
1134 end case;
1135 end if;
1137 -- Check if the current directive is pointless because of a previous,
1138 -- enclosing directive.
1140 for J in 1 .. Conditional_TOS loop
1141 if Conditional_Stack (J).Flag = Flag then
1142 Warning (Source_File, "directive without effect because of line"
1143 & Integer'Image (Conditional_Stack (J).Starting_Line));
1144 end if;
1145 end loop;
1147 Conditional_TOS := Conditional_TOS + 1;
1148 Conditional_Stack (Conditional_TOS) :=
1149 (Starting_Line => Source_File.Line,
1150 Cond => Cond,
1151 Flag => Flag,
1152 Excluding => Will_Exclude);
1153 end Push_Conditional;
1155 ---------------------
1156 -- Pop_Conditional --
1157 ---------------------
1159 procedure Pop_Conditional (Cond : Conditional) is
1160 begin
1161 if Conditional_TOS > 0 then
1162 case Cond is
1163 when Set =>
1164 if Conditional_Stack (Conditional_TOS).Cond /= Set then
1165 Error (Source_File,
1166 "'@end ifset' does not match '@ifclear' at line"
1167 & Integer'Image (Conditional_Stack
1168 (Conditional_TOS).Starting_Line));
1169 end if;
1171 when Clear =>
1172 if Conditional_Stack (Conditional_TOS).Cond /= Clear then
1173 Error (Source_File,
1174 "'@end ifclear' does not match '@ifset' at line"
1175 & Integer'Image (Conditional_Stack
1176 (Conditional_TOS).Starting_Line));
1177 end if;
1178 end case;
1180 Conditional_TOS := Conditional_TOS - 1;
1182 else
1183 case Cond is
1184 when Set =>
1185 Error (Source_File,
1186 "'@end ifset' without corresponding '@ifset'");
1188 when Clear =>
1189 Error (Source_File,
1190 "'@end ifclear' without corresponding '@ifclear'");
1191 end case;
1192 end if;
1193 end Pop_Conditional;
1195 -------------------------
1196 -- Currently_Excluding --
1197 -------------------------
1199 function Currently_Excluding return Boolean is
1200 begin
1201 return Conditional_TOS > 0
1202 and then Conditional_Stack (Conditional_TOS).Excluding;
1203 end Currently_Excluding;
1205 ----------------------------
1206 -- VMS_Context_Determined --
1207 ----------------------------
1209 function VMS_Context_Determined return Boolean is
1210 begin
1211 for J in 1 .. Conditional_TOS loop
1212 if Conditional_Stack (J).Flag = VMS then
1213 return True;
1214 end if;
1215 end loop;
1217 return False;
1218 end VMS_Context_Determined;
1220 --------------------
1221 -- In_VMS_Section --
1222 --------------------
1224 function In_VMS_Section return Boolean is
1225 begin
1226 for J in 1 .. Conditional_TOS loop
1227 if Conditional_Stack (J).Flag = VMS then
1228 return Conditional_Stack (J).Cond = Set;
1229 end if;
1230 end loop;
1232 return False;
1233 end In_VMS_Section;
1235 ----------------------------------
1236 -- Check_No_Pending_Conditional --
1237 ----------------------------------
1239 procedure Check_No_Pending_Conditional is
1240 begin
1241 for J in 1 .. Conditional_TOS loop
1242 case Conditional_Stack (J).Cond is
1243 when Set =>
1244 Error (Source_File, "Missing '@end ifset' for '@ifset' at line"
1245 & Integer'Image (Conditional_Stack (J).Starting_Line));
1247 when Clear =>
1248 Error (Source_File,
1249 "Missing '@end ifclear' for '@ifclear' at line"
1250 & Integer'Image (Conditional_Stack (J).Starting_Line));
1251 end case;
1252 end loop;
1253 end Check_No_Pending_Conditional;
1255 ------------------
1256 -- Main Program --
1257 ------------------
1259 Valid_Command_Line : Boolean;
1260 Output_File_Name : VString;
1262 begin
1263 Initialize_Extensions;
1265 Valid_Command_Line := Argument_Count in 3 .. 5;
1267 -- First argument: Target.
1269 if Valid_Command_Line then
1270 begin
1271 Target := Target_Type'Value (Argument (1));
1273 exception
1274 when Constraint_Error =>
1275 Valid_Command_Line := False;
1276 end;
1277 end if;
1279 -- Second argument: Source_File.
1281 if Valid_Command_Line then
1282 begin
1283 Source_File.Name := V (Argument (2));
1284 Open (Source_File.Data, In_File, Argument (2));
1286 exception
1287 when Name_Error =>
1288 Valid_Command_Line := False;
1289 end;
1290 end if;
1292 -- Third argument: Dictionary_File.
1294 if Valid_Command_Line then
1295 begin
1296 Dictionary_File.Name := V (Argument (3));
1297 Open (Dictionary_File.Data, In_File, Argument (3));
1299 exception
1300 when Name_Error =>
1301 Valid_Command_Line := False;
1302 end;
1303 end if;
1305 -- Fourth argument: Output_File.
1307 if Valid_Command_Line then
1308 if Argument_Count in 4 .. 5 then
1309 Output_File_Name := V (Argument (4));
1310 else
1311 case Target is
1312 when UNW =>
1313 Output_File_Name := V ("gnat_ugn_unw.texi");
1314 when VMS =>
1315 Output_File_Name := V ("gnat_ugn_vms.texi");
1316 end case;
1317 end if;
1319 Warnings_Enabled := Argument_Count = 5;
1321 begin
1322 Create (Output_File, Out_File, S (Output_File_Name));
1324 exception
1325 when Name_Error | Use_Error =>
1326 Valid_Command_Line := False;
1327 end;
1328 end if;
1330 if not Valid_Command_Line then
1331 Usage;
1332 Set_Exit_Status (Failure);
1334 else
1335 Read_Dictionary_File;
1336 Close (Dictionary_File.Data);
1338 -- Main processing starts here.
1340 Process_Source_File;
1341 Close (Output_File);
1342 Close (Source_File.Data);
1344 New_Line (Standard_Error);
1346 if Number_Of_Warnings = 0 then
1347 Put_Line (Standard_Error, " NO Warnings");
1349 else
1350 Put (Standard_Error, Integer'Image (Number_Of_Warnings));
1351 Put (Standard_Error, " Warning");
1353 if Number_Of_Warnings > 1 then
1354 Put (Standard_Error, "s");
1355 end if;
1357 New_Line (Standard_Error);
1358 end if;
1360 if Number_Of_Errors = 0 then
1361 Put_Line (Standard_Error, " NO Errors");
1363 else
1364 Put (Standard_Error, Integer'Image (Number_Of_Errors));
1365 Put (Standard_Error, " Error");
1367 if Number_Of_Errors > 1 then
1368 Put (Standard_Error, "s");
1369 end if;
1371 New_Line (Standard_Error);
1372 end if;
1374 if Number_Of_Errors /= 0 then
1375 Set_Exit_Status (Failure);
1376 else
1377 Set_Exit_Status (Success);
1378 end if;
1379 end if;
1380 end Xgnatugn;