File : xnmake.adb


   1 ------------------------------------------------------------------------------
   2 --                                                                          --
   3 --                          GNAT SYSTEM UTILITIES                           --
   4 --                                                                          --
   5 --                               X N M A K E                                --
   6 --                                                                          --
   7 --                                 B o d y                                  --
   8 --                                                                          --
   9 --          Copyright (C) 1992-2008, Free Software Foundation, Inc.         --
  10 --                                                                          --
  11 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
  12 -- terms of the  GNU General Public License as published  by the Free Soft- --
  13 -- ware  Foundation;  either version 3,  or (at your option) any later ver- --
  14 -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
  15 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
  16 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
  17 -- for  more details.  You should have  received  a copy of the GNU General --
  18 -- Public License  distributed with GNAT; see file COPYING3.  If not, go to --
  19 -- http://www.gnu.org/licenses for a complete copy of the license.          --
  20 --                                                                          --
  21 -- GNAT was originally developed  by the GNAT team at  New York University. --
  22 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
  23 --                                                                          --
  24 ------------------------------------------------------------------------------
  25 
  26 --  Program to construct the spec and body of the Nmake package
  27 
  28 --    Input files:
  29 
  30 --       sinfo.ads     Spec of Sinfo package
  31 --       nmake.adt     Template for Nmake package
  32 
  33 --    Output files:
  34 
  35 --       nmake.ads     Spec of Nmake package
  36 --       nmake.adb     Body of Nmake package
  37 
  38 --  Note: this program assumes that sinfo.ads has passed the error checks that
  39 --  are carried out by the csinfo utility, so it does not duplicate these
  40 --  checks and assumes that sinfo.ads has the correct form.
  41 
  42 --   In the absence of any switches, both the ads and adb files are output.
  43 --   The switch -s or /s indicates that only the ads file is to be output.
  44 --   The switch -b or /b indicates that only the adb file is to be output.
  45 
  46 --   If a file name argument is given, then the output is written to this file
  47 --   rather than to nmake.ads or nmake.adb. A file name can only be given if
  48 --   exactly one of the -s or -b options is present.
  49 
  50 with Ada.Command_Line;              use Ada.Command_Line;
  51 with Ada.Strings.Unbounded;         use Ada.Strings.Unbounded;
  52 with Ada.Strings.Unbounded.Text_IO; use Ada.Strings.Unbounded.Text_IO;
  53 with Ada.Strings.Maps;              use Ada.Strings.Maps;
  54 with Ada.Strings.Maps.Constants;    use Ada.Strings.Maps.Constants;
  55 with Ada.Streams.Stream_IO;         use Ada.Streams.Stream_IO;
  56 with Ada.Text_IO;                   use Ada.Text_IO;
  57 
  58 with GNAT.Spitbol;                  use GNAT.Spitbol;
  59 with GNAT.Spitbol.Patterns;         use GNAT.Spitbol.Patterns;
  60 
  61 with XUtil;
  62 
  63 procedure XNmake is
  64 
  65    Err : exception;
  66    --  Raised to terminate execution
  67 
  68    A        : VString := Nul;
  69    Arg      : VString := Nul;
  70    Arg_List : VString := Nul;
  71    Comment  : VString := Nul;
  72    Default  : VString := Nul;
  73    Field    : VString := Nul;
  74    Line     : VString := Nul;
  75    Node     : VString := Nul;
  76    Op_Name  : VString := Nul;
  77    Prevl    : VString := Nul;
  78    Synonym  : VString := Nul;
  79    X        : VString := Nul;
  80 
  81    NWidth : Natural;
  82 
  83    FileS : VString := V ("nmake.ads");
  84    FileB : VString := V ("nmake.adb");
  85    --  Set to null if corresponding file not to be generated
  86 
  87    Given_File : VString := Nul;
  88    --  File name given by command line argument
  89 
  90    subtype Sfile is Ada.Streams.Stream_IO.File_Type;
  91 
  92    InS,  InT  : Ada.Text_IO.File_Type;
  93    OutS, OutB : Sfile;
  94 
  95    wsp : constant Pattern := Span (' ' & ASCII.HT);
  96 
  97    Body_Only : constant Pattern := BreakX (' ') * X
  98                                    & Span (' ') & "--  body only";
  99    Spec_Only : constant Pattern := BreakX (' ') * X
 100                                    & Span (' ') & "--  spec only";
 101 
 102    Node_Hdr  : constant Pattern := wsp & "--  N_" & Rest * Node;
 103    Punc      : constant Pattern := BreakX (" .,");
 104 
 105    Binop     : constant Pattern := wsp
 106                                    & "--  plus fields for binary operator";
 107    Unop      : constant Pattern := wsp
 108                                    & "--  plus fields for unary operator";
 109    Syn       : constant Pattern := wsp & "--  " & Break (' ') * Synonym
 110                                    & " (" & Break (')') * Field
 111                                    & Rest * Comment;
 112 
 113    Templ     : constant Pattern := BreakX ('T') * A & "T e m p l a t e";
 114    Spec      : constant Pattern := BreakX ('S') * A & "S p e c";
 115 
 116    Sem_Field : constant Pattern := BreakX ('-') & "-Sem";
 117    Lib_Field : constant Pattern := BreakX ('-') & "-Lib";
 118 
 119    Get_Field : constant Pattern := BreakX (Decimal_Digit_Set) * Field;
 120 
 121    Get_Dflt  : constant Pattern := BreakX ('(') & "(set to "
 122                                    & Break (" ") * Default & " if";
 123 
 124    Next_Arg  : constant Pattern := Break (',') * Arg & ',';
 125 
 126    Op_Node   : constant Pattern := "Op_" & Rest * Op_Name;
 127 
 128    Shft_Rot  : constant Pattern := "Shift_" or "Rotate_";
 129 
 130    No_Ent    : constant Pattern := "Or_Else" or "And_Then"
 131                                      or "In" or "Not_In";
 132 
 133    M : Match_Result;
 134 
 135    V_String_Id : constant VString := V ("String_Id");
 136    V_Node_Id   : constant VString := V ("Node_Id");
 137    V_Name_Id   : constant VString := V ("Name_Id");
 138    V_List_Id   : constant VString := V ("List_Id");
 139    V_Elist_Id  : constant VString := V ("Elist_Id");
 140    V_Boolean   : constant VString := V ("Boolean");
 141 
 142    procedure Put_Line (F : Sfile; S : String)  renames XUtil.Put_Line;
 143    procedure Put_Line (F : Sfile; S : VString) renames XUtil.Put_Line;
 144    --  Local version of Put_Line ensures Unix style line endings
 145 
 146    procedure WriteS  (S : String);
 147    procedure WriteB  (S : String);
 148    procedure WriteBS (S : String);
 149    procedure WriteS  (S : VString);
 150    procedure WriteB  (S : VString);
 151    procedure WriteBS (S : VString);
 152    --  Write given line to spec or body file or both if active
 153 
 154    procedure WriteB (S : String) is
 155    begin
 156       if FileB /= Nul then
 157          Put_Line (OutB, S);
 158       end if;
 159    end WriteB;
 160 
 161    procedure WriteB (S : VString) is
 162    begin
 163       if FileB /= Nul then
 164          Put_Line (OutB, S);
 165       end if;
 166    end WriteB;
 167 
 168    procedure WriteBS (S : String) is
 169    begin
 170       if FileB /= Nul then
 171          Put_Line (OutB, S);
 172       end if;
 173 
 174       if FileS /= Nul then
 175          Put_Line (OutS, S);
 176       end if;
 177    end WriteBS;
 178 
 179    procedure WriteBS (S : VString) is
 180    begin
 181       if FileB /= Nul then
 182          Put_Line (OutB, S);
 183       end if;
 184 
 185       if FileS /= Nul then
 186          Put_Line (OutS, S);
 187       end if;
 188    end WriteBS;
 189 
 190    procedure WriteS (S : String) is
 191    begin
 192       if FileS /= Nul then
 193          Put_Line (OutS, S);
 194       end if;
 195    end WriteS;
 196 
 197    procedure WriteS (S : VString) is
 198    begin
 199       if FileS /= Nul then
 200          Put_Line (OutS, S);
 201       end if;
 202    end WriteS;
 203 
 204 --  Start of processing for XNmake
 205 
 206 begin
 207    NWidth := 28;
 208    Anchored_Mode := True;
 209 
 210    for ArgN in 1 .. Argument_Count loop
 211       declare
 212          Arg : constant String := Argument (ArgN);
 213 
 214       begin
 215          if Arg (1) = '-' then
 216             if Arg'Length = 2
 217               and then (Arg (2) = 'b' or else Arg (2) = 'B')
 218             then
 219                FileS := Nul;
 220 
 221             elsif Arg'Length = 2
 222               and then (Arg (2) = 's' or else Arg (2) = 'S')
 223             then
 224                FileB := Nul;
 225 
 226             else
 227                raise Err;
 228             end if;
 229 
 230          else
 231             if Given_File /= Nul then
 232                raise Err;
 233             else
 234                Given_File := V (Arg);
 235             end if;
 236          end if;
 237       end;
 238    end loop;
 239 
 240    if FileS = Nul and then FileB = Nul then
 241       raise Err;
 242 
 243    elsif Given_File /= Nul then
 244       if FileB = Nul then
 245          FileS := Given_File;
 246 
 247       elsif FileS = Nul then
 248          FileB := Given_File;
 249 
 250       else
 251          raise Err;
 252       end if;
 253    end if;
 254 
 255    Open (InS, In_File, "sinfo.ads");
 256    Open (InT, In_File, "nmake.adt");
 257 
 258    if FileS /= Nul then
 259       Create (OutS, Out_File, S (FileS));
 260    end if;
 261 
 262    if FileB /= Nul then
 263       Create (OutB, Out_File, S (FileB));
 264    end if;
 265 
 266    Anchored_Mode := True;
 267 
 268    --  Copy initial part of template to spec and body
 269 
 270    loop
 271       Line := Get_Line (InT);
 272 
 273       --  Skip lines describing the template
 274 
 275       if Match (Line, "--  This file is a template") then
 276          loop
 277             Line := Get_Line (InT);
 278             exit when Line = "";
 279          end loop;
 280       end if;
 281 
 282       --  Loop keeps going until "package" keyword written
 283 
 284       exit when Match (Line, "package");
 285 
 286       --  Deal with WITH lines, writing to body or spec as appropriate
 287 
 288       if Match (Line, Body_Only, M) then
 289          Replace (M, X);
 290          WriteB (Line);
 291 
 292       elsif Match (Line, Spec_Only, M) then
 293          Replace (M, X);
 294          WriteS (Line);
 295 
 296       --  Change header from Template to Spec and write to spec file
 297 
 298       else
 299          if Match (Line, Templ, M) then
 300             Replace (M, A &  "    S p e c    ");
 301          end if;
 302 
 303          WriteS (Line);
 304 
 305          --  Write header line to body file
 306 
 307          if Match (Line, Spec, M) then
 308             Replace (M, A &  "B o d y");
 309          end if;
 310 
 311          WriteB (Line);
 312       end if;
 313    end loop;
 314 
 315    --  Package line reached
 316 
 317    WriteS ("package Nmake is");
 318    WriteB ("package body Nmake is");
 319    WriteB ("");
 320 
 321    --  Copy rest of lines up to template insert point to spec only
 322 
 323    loop
 324       Line := Get_Line (InT);
 325       exit when Match (Line, "!!TEMPLATE INSERTION POINT");
 326       WriteS (Line);
 327    end loop;
 328 
 329    --  Here we are doing the actual insertions, loop through node types
 330 
 331    loop
 332       Line := Get_Line (InS);
 333 
 334       if Match (Line, Node_Hdr)
 335         and then not Match (Node, Punc)
 336         and then Node /= "Unused"
 337       then
 338          exit when Node = "Empty";
 339          Prevl := "   function Make_" & Node & " (Sloc : Source_Ptr";
 340          Arg_List := Nul;
 341 
 342          --  Loop through fields of one node
 343 
 344          loop
 345             Line := Get_Line (InS);
 346             exit when Line = "";
 347 
 348             if Match (Line, Binop) then
 349                WriteBS (Prevl & ';');
 350                Append (Arg_List, "Left_Opnd,Right_Opnd,");
 351                WriteBS (
 352                  "      " & Rpad ("Left_Opnd",  NWidth) & " : Node_Id;");
 353                Prevl :=
 354                  "      " & Rpad ("Right_Opnd", NWidth) & " : Node_Id";
 355 
 356             elsif Match (Line, Unop) then
 357                WriteBS (Prevl & ';');
 358                Append (Arg_List, "Right_Opnd,");
 359                Prevl := "      " & Rpad ("Right_Opnd", NWidth) & " : Node_Id";
 360 
 361             elsif Match (Line, Syn) then
 362                if         Synonym /= "Prev_Ids"
 363                  and then Synonym /= "More_Ids"
 364                  and then Synonym /= "Comes_From_Source"
 365                  and then Synonym /= "Paren_Count"
 366                  and then not Match (Field, Sem_Field)
 367                  and then not Match (Field, Lib_Field)
 368                then
 369                   Match (Field, Get_Field);
 370 
 371                   if    Field = "Str"   then
 372                      Field := V_String_Id;
 373                   elsif Field = "Node"  then
 374                      Field := V_Node_Id;
 375                   elsif Field = "Name"  then
 376                      Field := V_Name_Id;
 377                   elsif Field = "List"  then
 378                      Field := V_List_Id;
 379                   elsif Field = "Elist" then
 380                      Field := V_Elist_Id;
 381                   elsif Field = "Flag"  then
 382                      Field := V_Boolean;
 383                   end if;
 384 
 385                   if Field = "Boolean" then
 386                      Default := V ("False");
 387                   else
 388                      Default := Nul;
 389                   end if;
 390 
 391                   Match (Comment, Get_Dflt);
 392 
 393                   WriteBS (Prevl & ';');
 394                   Append (Arg_List, Synonym & ',');
 395                   Rpad (Synonym, NWidth);
 396 
 397                   if Default = "" then
 398                      Prevl := "      " & Synonym & " : " & Field;
 399                   else
 400                      Prevl :=
 401                        "      " & Synonym & " : " & Field & " := " & Default;
 402                   end if;
 403                end if;
 404             end if;
 405          end loop;
 406 
 407          WriteBS (Prevl & ')');
 408          WriteS ("      return Node_Id;");
 409          WriteS ("   pragma Inline (Make_" & Node & ");");
 410          WriteB ("      return Node_Id");
 411          WriteB ("   is");
 412          WriteB ("      N : constant Node_Id :=");
 413 
 414          if Match (Node, "Defining_Identifier") or else
 415             Match (Node, "Defining_Character")  or else
 416             Match (Node, "Defining_Operator")
 417          then
 418             WriteB ("            New_Entity (N_" & Node & ", Sloc);");
 419          else
 420             WriteB ("            New_Node (N_" & Node & ", Sloc);");
 421          end if;
 422 
 423          WriteB ("   begin");
 424 
 425          while Match (Arg_List, Next_Arg, "") loop
 426             if Length (Arg) < NWidth then
 427                WriteB ("      Set_" & Arg & " (N, " & Arg & ");");
 428             else
 429                WriteB ("      Set_" & Arg);
 430                WriteB ("        (N, " & Arg & ");");
 431             end if;
 432          end loop;
 433 
 434          if Match (Node, Op_Node) then
 435             if Node = "Op_Plus" then
 436                WriteB ("      Set_Chars (N, Name_Op_Add);");
 437 
 438             elsif Node = "Op_Minus" then
 439                WriteB ("      Set_Chars (N, Name_Op_Subtract);");
 440 
 441             elsif Match (Op_Name, Shft_Rot) then
 442                WriteB ("      Set_Chars (N, Name_" & Op_Name & ");");
 443 
 444             else
 445                WriteB ("      Set_Chars (N, Name_" & Node & ");");
 446             end if;
 447 
 448             if not Match (Op_Name, No_Ent) then
 449                WriteB ("      Set_Entity (N, Standard_" & Node & ");");
 450             end if;
 451          end if;
 452 
 453          WriteB ("      return N;");
 454          WriteB ("   end Make_" & Node & ';');
 455          WriteBS ("");
 456       end if;
 457    end loop;
 458 
 459    WriteBS ("end Nmake;");
 460 
 461 exception
 462 
 463    when Err =>
 464       Put_Line (Standard_Error, "usage: xnmake [-b] [-s] [filename]");
 465       Set_Exit_Status (1);
 466 
 467 end XNmake;