File : sfn_scan.adb


   1 ------------------------------------------------------------------------------
   2 --                                                                          --
   3 --                         GNAT COMPILER COMPONENTS                         --
   4 --                                                                          --
   5 --                             S F N _ S C A N                              --
   6 --                                                                          --
   7 --                                 B o d y                                  --
   8 --                                                                          --
   9 --          Copyright (C) 2000-2010, 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.                                     --
  17 --                                                                          --
  18 --                                                                          --
  19 --                                                                          --
  20 --                                                                          --
  21 --                                                                          --
  22 -- You should have received a copy of the GNU General Public License and    --
  23 -- a copy of the GCC Runtime Library Exception along with this program;     --
  24 -- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
  25 -- <http://www.gnu.org/licenses/>.                                          --
  26 --                                                                          --
  27 -- GNAT was originally developed  by the GNAT team at  New York University. --
  28 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
  29 --                                                                          --
  30 ------------------------------------------------------------------------------
  31 
  32 with Ada.Exceptions; use Ada.Exceptions;
  33 
  34 package body SFN_Scan is
  35 
  36    use ASCII;
  37    --  Allow easy access to control character definitions
  38 
  39    EOF : constant Character := ASCII.SUB;
  40    --  The character SUB (16#1A#) is used in DOS-derived systems, such as
  41    --  Windows to signal the end of a text file. If this character appears as
  42    --  the last character of a file scanned by a call to Scan_SFN_Pragmas, then
  43    --  it is ignored, otherwise it is treated as an illegal character.
  44 
  45    type String_Ptr is access String;
  46 
  47    S : String_Ptr;
  48    --  Points to the gnat.adc input file
  49 
  50    P : Natural;
  51    --  Subscript of next character to process in S
  52 
  53    Line_Num : Natural;
  54    --  Current line number
  55 
  56    Start_Of_Line : Natural;
  57    --  Subscript of first character at start of current line
  58 
  59    ----------------------
  60    -- Local Procedures --
  61    ----------------------
  62 
  63    function Acquire_Integer return Natural;
  64    --  This function skips white space, and then scans and returns
  65    --  an unsigned integer. Raises Error if no integer is present
  66    --  or if the integer is greater than 999.
  67 
  68    function Acquire_String (B : Natural; E : Natural) return String;
  69    --  This function takes a string scanned out by Scan_String, strips
  70    --  the enclosing quote characters and any internal doubled quote
  71    --  characters, and returns the result as a String. The arguments
  72    --  B and E are as returned from a call to Scan_String. The lower
  73    --  bound of the string returned is always 1.
  74 
  75    function Acquire_Unit_Name return String;
  76    --  Skips white space, and then scans and returns a unit name. The
  77    --  unit name is cased exactly as it appears in the source file.
  78    --  The terminating character must be white space, or a comma or
  79    --  a right parenthesis or end of file.
  80 
  81    function At_EOF return Boolean;
  82    pragma Inline (At_EOF);
  83    --  Returns True if at end of file, False if not. Note that this
  84    --  function does NOT skip white space, so P is always unchanged.
  85 
  86    procedure Check_Not_At_EOF;
  87    pragma Inline (Check_Not_At_EOF);
  88    --  Skips past white space if any, and then raises Error if at
  89    --  end of file. Otherwise returns with P skipped past whitespace.
  90 
  91    function Check_File_Type return Character;
  92    --  Skips white space if any, and then looks for any of the tokens
  93    --  Spec_File_Name, Body_File_Name, or Subunit_File_Name. If one
  94    --  of these is found then the value returned is 's', 'b' or 'u'
  95    --  respectively, and P is bumped past the token. If none of
  96    --  these tokens is found, then P is unchanged (except for
  97    --  possible skip of white space), and a space is returned.
  98 
  99    function Check_Token (T : String) return Boolean;
 100    --  Skips white space if any, and then checks if the string at the
 101    --  current location matches the given string T, and the character
 102    --  immediately following is non-alphabetic, non-numeric. If so,
 103    --  P is stepped past the token, and True is returned. If not,
 104    --  P is unchanged (except for possibly skipping past whitespace),
 105    --  and False is returned. S may contain only lower-case letters
 106    --  ('a' .. 'z').
 107 
 108    procedure Error (Err : String);
 109    --  Called if an error is detected. Raises Syntax_Error_In_GNAT_ADC
 110    --  with a message of the form gnat.adc:line:col: xxx, where xxx is
 111    --  the string Err passed as a parameter.
 112 
 113    procedure Require_Token (T : String);
 114    --  Skips white space if any, and then requires the given string
 115    --  to be present. If it is, the P is stepped past it, otherwise
 116    --  Error is raised, since this is a syntax error. Require_Token
 117    --  is used only for sequences of special characters, so there
 118    --  is no issue of terminators, or casing of letters.
 119 
 120    procedure Scan_String (B : out Natural; E : out Natural);
 121    --  Skips white space if any, then requires that a double quote
 122    --  or percent be present (start of string). Raises error if
 123    --  neither of these two characters is found. Otherwise scans
 124    --  out the string, and returns with P pointing past the
 125    --  closing quote and S (B .. E) contains the characters of the
 126    --  string (including the enclosing quotes, with internal quotes
 127    --  still doubled). Raises Error if the string is malformed.
 128 
 129    procedure Skip_WS;
 130    --  Skips P past any white space characters (end of line
 131    --  characters, spaces, comments, horizontal tab characters).
 132 
 133    ---------------------
 134    -- Acquire_Integer --
 135    ---------------------
 136 
 137    function Acquire_Integer return Natural is
 138       N : Natural := 0;
 139 
 140    begin
 141       Skip_WS;
 142 
 143       if S (P) not in '0' .. '9' then
 144          Error ("missing index parameter");
 145       end if;
 146 
 147       while S (P) in '0' .. '9' loop
 148          N := N * 10 + Character'Pos (S (P)) - Character'Pos ('0');
 149 
 150          if N > 999 then
 151             Error ("index value greater than 999");
 152          end if;
 153 
 154          P := P + 1;
 155       end loop;
 156 
 157       return N;
 158    end Acquire_Integer;
 159 
 160    --------------------
 161    -- Acquire_String --
 162    --------------------
 163 
 164    function Acquire_String (B : Natural; E : Natural) return String is
 165       Str : String (1 .. E - B - 1);
 166       Q   : constant Character := S (B);
 167       J   : Natural;
 168       Ptr : Natural;
 169 
 170    begin
 171       Ptr := B + 1;
 172       J := 0;
 173       while Ptr < E loop
 174          J := J + 1;
 175          Str (J) := S (Ptr);
 176 
 177          if S (Ptr) = Q and then S (Ptr + 1) = Q then
 178             Ptr := Ptr + 2;
 179          else
 180             Ptr := Ptr + 1;
 181          end if;
 182       end loop;
 183 
 184       return Str (1 .. J);
 185    end Acquire_String;
 186 
 187    -----------------------
 188    -- Acquire_Unit_Name --
 189    -----------------------
 190 
 191    function Acquire_Unit_Name return String is
 192       B : Natural;
 193 
 194    begin
 195       Check_Not_At_EOF;
 196       B := P;
 197 
 198       while not At_EOF loop
 199          exit when S (P) not in '0' .. '9'
 200            and then S (P) /= '.'
 201            and then S (P) /= '_'
 202            and then not (S (P) = '[' and then S (P + 1) = '"')
 203            and then not (S (P) = '"' and then S (P - 1) = '[')
 204            and then not (S (P) = '"' and then S (P + 1) = ']')
 205            and then not (S (P) = ']' and then S (P - 1) = '"')
 206            and then S (P) < 'A';
 207          P := P + 1;
 208       end loop;
 209 
 210       if P = B then
 211          Error ("null unit name");
 212       end if;
 213 
 214       return S (B .. P - 1);
 215    end Acquire_Unit_Name;
 216 
 217    ------------
 218    -- At_EOF --
 219    ------------
 220 
 221    function At_EOF return Boolean is
 222    begin
 223       --  Immediate return (False) if before last character of file
 224 
 225       if P < S'Last then
 226          return False;
 227 
 228       --  Special case: DOS EOF character as last character of file is
 229       --  allowed and treated as an end of file.
 230 
 231       elsif P = S'Last then
 232          return S (P) = EOF;
 233 
 234       --  If beyond last character of file, then definitely at EOF
 235 
 236       else
 237          return True;
 238       end if;
 239    end At_EOF;
 240 
 241    ---------------------
 242    -- Check_File_Type --
 243    ---------------------
 244 
 245    function Check_File_Type return Character is
 246    begin
 247       if Check_Token ("spec_file_name") then
 248          return 's';
 249       elsif Check_Token ("body_file_name") then
 250          return 'b';
 251       elsif Check_Token ("subunit_file_name") then
 252          return 'u';
 253       else
 254          return ' ';
 255       end if;
 256    end Check_File_Type;
 257 
 258    ----------------------
 259    -- Check_Not_At_EOF --
 260    ----------------------
 261 
 262    procedure Check_Not_At_EOF is
 263    begin
 264       Skip_WS;
 265 
 266       if At_EOF then
 267          Error ("unexpected end of file");
 268       end if;
 269 
 270       return;
 271    end Check_Not_At_EOF;
 272 
 273    -----------------
 274    -- Check_Token --
 275    -----------------
 276 
 277    function Check_Token (T : String) return Boolean is
 278       Save_P : Natural;
 279       C : Character;
 280 
 281    begin
 282       Skip_WS;
 283       Save_P := P;
 284 
 285       for K in T'Range loop
 286          if At_EOF then
 287             P := Save_P;
 288             return False;
 289          end if;
 290 
 291          C := S (P);
 292 
 293          if C in 'A' .. 'Z' then
 294             C := Character'Val (Character'Pos (C) +
 295                                  (Character'Pos ('a') - Character'Pos ('A')));
 296          end if;
 297 
 298          if C /= T (K) then
 299             P := Save_P;
 300             return False;
 301          end if;
 302 
 303          P := P + 1;
 304       end loop;
 305 
 306       if At_EOF then
 307          return True;
 308       end if;
 309 
 310       C := S (P);
 311 
 312       if C in '0' .. '9'
 313         or else C in 'a' .. 'z'
 314         or else C in 'A' .. 'Z'
 315         or else C > Character'Val (127)
 316       then
 317          P := Save_P;
 318          return False;
 319 
 320       else
 321          return True;
 322       end if;
 323    end Check_Token;
 324 
 325    -----------
 326    -- Error --
 327    -----------
 328 
 329    procedure Error (Err : String) is
 330       C : Natural := 0;
 331       --  Column number
 332 
 333       M : String (1 .. 80);
 334       --  Buffer used to build resulting error msg
 335 
 336       LM : Natural := 0;
 337       --  Pointer to last set location in M
 338 
 339       procedure Add_Nat (N : Natural);
 340       --  Add chars of integer to error msg buffer
 341 
 342       -------------
 343       -- Add_Nat --
 344       -------------
 345 
 346       procedure Add_Nat (N : Natural) is
 347       begin
 348          if N > 9 then
 349             Add_Nat (N / 10);
 350          end if;
 351 
 352          LM := LM + 1;
 353          M (LM) := Character'Val (N mod 10 + Character'Pos ('0'));
 354       end Add_Nat;
 355 
 356    --  Start of processing for Error
 357 
 358    begin
 359       M (1 .. 9) := "gnat.adc:";
 360       LM := 9;
 361       Add_Nat (Line_Num);
 362       LM := LM + 1;
 363       M (LM) := ':';
 364 
 365       --  Determine column number
 366 
 367       for X in Start_Of_Line .. P loop
 368          C := C + 1;
 369 
 370          if S (X) = HT then
 371             C := (C + 7) / 8 * 8;
 372          end if;
 373       end loop;
 374 
 375       Add_Nat (C);
 376       M (LM + 1) := ':';
 377       LM := LM + 1;
 378       M (LM + 1) := ' ';
 379       LM := LM + 1;
 380 
 381       M (LM + 1 .. LM + Err'Length) := Err;
 382       LM := LM + Err'Length;
 383 
 384       Raise_Exception (Syntax_Error_In_GNAT_ADC'Identity, M (1 .. LM));
 385    end Error;
 386 
 387    -------------------
 388    -- Require_Token --
 389    -------------------
 390 
 391    procedure Require_Token (T : String) is
 392       SaveP : Natural;
 393 
 394    begin
 395       Skip_WS;
 396       SaveP := P;
 397 
 398       for J in T'Range loop
 399 
 400          if At_EOF or else S (P) /= T (J) then
 401             declare
 402                S : String (1 .. T'Length + 10);
 403 
 404             begin
 405                S (1 .. 9) := "missing """;
 406                S (10 .. T'Length + 9) := T;
 407                S (T'Length + 10) := '"';
 408                P := SaveP;
 409                Error (S);
 410             end;
 411 
 412          else
 413             P := P + 1;
 414          end if;
 415       end loop;
 416    end Require_Token;
 417 
 418    ----------------------
 419    -- Scan_SFN_Pragmas --
 420    ----------------------
 421 
 422    procedure Scan_SFN_Pragmas
 423      (Source   : String;
 424       SFN_Ptr  : Set_File_Name_Ptr;
 425       SFNP_Ptr : Set_File_Name_Pattern_Ptr)
 426    is
 427       B, E : Natural;
 428       Typ  : Character;
 429       Cas  : Character;
 430 
 431    begin
 432       Line_Num := 1;
 433       S := Source'Unrestricted_Access;
 434       P := Source'First;
 435       Start_Of_Line := P;
 436 
 437       --  Loop through pragmas in file
 438 
 439       Main_Scan_Loop : loop
 440          Skip_WS;
 441          exit Main_Scan_Loop when At_EOF;
 442 
 443          --  Error if something other than pragma
 444 
 445          if not Check_Token ("pragma") then
 446             Error ("non pragma encountered");
 447          end if;
 448 
 449          --  Source_File_Name pragma case
 450 
 451          if Check_Token ("source_file_name")
 452               or else
 453              Check_Token ("source_file_name_project")
 454          then
 455             Require_Token ("(");
 456 
 457             Typ := Check_File_Type;
 458 
 459             --  First format, with unit name first
 460 
 461             if Typ = ' ' then
 462                if Check_Token ("unit_name") then
 463                   Require_Token ("=>");
 464                end if;
 465 
 466                declare
 467                   U : constant String := Acquire_Unit_Name;
 468 
 469                begin
 470                   Require_Token (",");
 471                   Typ := Check_File_Type;
 472 
 473                   if Typ /= 's' and then Typ /= 'b' then
 474                      Error ("bad pragma");
 475                   end if;
 476 
 477                   Require_Token ("=>");
 478                   Scan_String (B, E);
 479 
 480                   declare
 481                      F : constant String := Acquire_String (B, E);
 482                      X : Natural;
 483 
 484                   begin
 485                      --  Scan Index parameter if present
 486 
 487                      if Check_Token (",") then
 488                         if Check_Token ("index") then
 489                            Require_Token ("=>");
 490                         end if;
 491 
 492                         X := Acquire_Integer;
 493                      else
 494                         X := 0;
 495                      end if;
 496 
 497                      Require_Token (")");
 498                      Require_Token (";");
 499                      SFN_Ptr.all (Typ, U, F, X);
 500                   end;
 501                end;
 502 
 503             --  Second format with pattern string
 504 
 505             else
 506                Require_Token ("=>");
 507                Scan_String (B, E);
 508 
 509                declare
 510                   Pat : constant String := Acquire_String (B, E);
 511                   Nas : Natural := 0;
 512 
 513                begin
 514                   --  Check exactly one asterisk
 515 
 516                   for J in Pat'Range loop
 517                      if Pat (J) = '*' then
 518                         Nas := Nas + 1;
 519                      end if;
 520                   end loop;
 521 
 522                   if Nas /= 1 then
 523                      Error ("** not allowed");
 524                   end if;
 525 
 526                   B := 0;
 527                   E := 0;
 528                   Cas := ' ';
 529 
 530                   --  Loop to scan out Casing or Dot_Replacement parameters
 531 
 532                   loop
 533                      Check_Not_At_EOF;
 534                      exit when S (P) = ')';
 535                      Require_Token (",");
 536 
 537                      if Check_Token ("casing") then
 538                         Require_Token ("=>");
 539 
 540                         if Cas /= ' ' then
 541                            Error ("duplicate casing argument");
 542                         elsif Check_Token ("lowercase") then
 543                            Cas := 'l';
 544                         elsif Check_Token ("uppercase") then
 545                            Cas := 'u';
 546                         elsif Check_Token ("mixedcase") then
 547                            Cas := 'm';
 548                         else
 549                            Error ("invalid casing argument");
 550                         end if;
 551 
 552                      elsif Check_Token ("dot_replacement") then
 553                         Require_Token ("=>");
 554 
 555                         if E /= 0 then
 556                            Error ("duplicate dot_replacement");
 557                         else
 558                            Scan_String (B, E);
 559                         end if;
 560 
 561                      else
 562                         Error ("invalid argument");
 563                      end if;
 564                   end loop;
 565 
 566                   Require_Token (")");
 567                   Require_Token (";");
 568 
 569                   if Cas = ' ' then
 570                      Cas := 'l';
 571                   end if;
 572 
 573                   if E = 0 then
 574                      SFNP_Ptr.all (Pat, Typ, ".", Cas);
 575 
 576                   else
 577                      declare
 578                         Dot : constant String := Acquire_String (B, E);
 579 
 580                      begin
 581                         SFNP_Ptr.all (Pat, Typ, Dot, Cas);
 582                      end;
 583                   end if;
 584                end;
 585             end if;
 586 
 587          --  Some other pragma, scan to semicolon at end of pragma
 588 
 589          else
 590             Skip_Loop : loop
 591                exit Main_Scan_Loop when At_EOF;
 592                exit Skip_Loop when S (P) = ';';
 593 
 594                if S (P) = '"' or else S (P) = '%' then
 595                   Scan_String (B, E);
 596                else
 597                   P := P + 1;
 598                end if;
 599             end loop Skip_Loop;
 600 
 601             --  We successfully skipped to semicolon, so skip past it
 602 
 603             P := P + 1;
 604          end if;
 605       end loop Main_Scan_Loop;
 606 
 607    exception
 608       when others =>
 609          Cursor := P - S'First + 1;
 610          raise;
 611    end Scan_SFN_Pragmas;
 612 
 613    -----------------
 614    -- Scan_String --
 615    -----------------
 616 
 617    procedure Scan_String (B : out Natural; E : out Natural) is
 618       Q : Character;
 619 
 620    begin
 621       Check_Not_At_EOF;
 622 
 623       if S (P) = '"' then
 624          Q := '"';
 625       elsif S (P) = '%' then
 626          Q := '%';
 627       else
 628          Error ("bad string");
 629          Q := '"';
 630       end if;
 631 
 632       --  Scan out the string, B points to first char
 633 
 634       B := P;
 635       P := P + 1;
 636 
 637       loop
 638          if At_EOF or else S (P) = LF or else S (P) = CR then
 639             Error -- CODEFIX
 640               ("missing string quote");
 641 
 642          elsif S (P) = HT then
 643             Error ("tab character in string");
 644 
 645          elsif S (P) /= Q then
 646             P := P + 1;
 647 
 648          --  We have a quote
 649 
 650          else
 651             P := P + 1;
 652 
 653             --  Check for doubled quote
 654 
 655             if not At_EOF and then S (P) = Q then
 656                P := P + 1;
 657 
 658             --  Otherwise this is the terminating quote
 659 
 660             else
 661                E := P - 1;
 662                return;
 663             end if;
 664          end if;
 665       end loop;
 666    end Scan_String;
 667 
 668    -------------
 669    -- Skip_WS --
 670    -------------
 671 
 672    procedure Skip_WS is
 673    begin
 674       WS_Scan : while not At_EOF loop
 675          case S (P) is
 676 
 677             --  End of physical line
 678 
 679             when CR | LF =>
 680                Line_Num := Line_Num + 1;
 681                P := P + 1;
 682 
 683                while not At_EOF
 684                  and then (S (P) = CR or else S (P) = LF)
 685                loop
 686                   Line_Num := Line_Num + 1;
 687                   P := P + 1;
 688                end loop;
 689 
 690                Start_Of_Line := P;
 691 
 692             --  All other cases of white space characters
 693 
 694             when ' ' | FF | VT | HT =>
 695                P := P + 1;
 696 
 697             --  Comment
 698 
 699             when '-' =>
 700                P := P + 1;
 701 
 702                if At_EOF then
 703                   Error ("bad comment");
 704 
 705                elsif S (P) = '-' then
 706                   P := P + 1;
 707 
 708                   while not At_EOF loop
 709                      case S (P) is
 710                         when CR | LF | FF | VT =>
 711                            exit;
 712                         when others =>
 713                            P := P + 1;
 714                      end case;
 715                   end loop;
 716 
 717                else
 718                   P := P - 1;
 719                   exit WS_Scan;
 720                end if;
 721 
 722             when others =>
 723                exit WS_Scan;
 724 
 725          end case;
 726       end loop WS_Scan;
 727    end Skip_WS;
 728 
 729 end SFN_Scan;