--------------------------------------------------------------
-- Ada Generator for Object-Oriented Parsers (AdaGOOP)
--
-- PACKAGE Generate
-- Main generation routines
--
-- By: Martin C. Carlisle
--     United States Air Force Academy
--     Department of Computer Science
--
-- main routine is Process_File (at bottom)
--------------------------------------------------------------
with Ada.Strings.Unbounded;
use Ada.Strings.Unbounded;

with Ada.Characters.Handling;
use Ada.Characters.Handling;

with Ada.Exceptions;

with String_Parsing;
with File_Helpers;
with List;
with Hash;
with Limits;

with Ada.Text_Io;
use Ada.Text_Io;

use type Ada.Text_IO.Positive_Count;

package body Generate is
   Macro_Section_Header   : constant String := "token_macros";
   Token_Section_Header   : constant String := "tokens";
   Grammar_Section_Header : constant String := "grammar";
   Ignored_Token_Marker   : constant String := "ignore";
   Method_List_Marker     : constant String := "global_methods";
--   Visitor_List_Marker    : constant String := "visitors";
   Start_Symbol_Marker    : constant String := "%start";
   Bad_File : exception;
   
   type Production_Names is array(1..Limits.Symbols_Per_Production) of Unbounded_String;
   type Production_Record is record
      Symbols : Production_Names;
      Length  : Natural := 0;
   end record;
   
   type Production_Record_Array is array(1..Limits.Productions_Per_Nonterminal)
      of Production_Record;
   type Nonterminal_Record is record
      Symbol : Unbounded_String;
      Productions : Production_Record_Array;
      Length : Natural := 1; 
      -- will always have at least 1 production
      -- and it is easier to post increment
   end record;
   
   package String_List is new List(Unbounded_String);
   
   -- remember the name of this file.  Append "model" for tree file
   Unbounded_Prefix : Unbounded_String;
   
   procedure Autogenerated_Message(File : in File_Type) is
   begin
      Ada.Text_IO.Put_Line(File,
         "-- Automatically generated file");
      Ada.Text_IO.Put_Line(File,
         "-- from AdaGOOP2005 (by Martin C. Carlisle)");
      Ada.Text_IO.Put_Line(File,
         "-- If modified, be careful about losing work.");
   end Autogenerated_Message;
   
   -- same as integer'image w/o leading blank or '-'
   function Integer_Image(X : in Integer) return String is
      Result : String := Integer'Image(X);
   begin
      return Result(Result'First+1..Result'Last);
   end Integer_Image;
   
   -- Symbols(i) concatenated with occurrence number
   function Portion(
        Symbols : in Production_Names;
        I       : in Natural;
        Length  : in Natural) return String is
      Count  : Natural := 0;
      Number : Natural := 1;
   begin
      for j in 1..Length loop
         if i/=j and then 
            To_Lower(To_String(Symbols(i)))=To_Lower(To_String(Symbols(j))) then
            Count := Count + 1;
            if j<i then
               Number := Number + 1;
            end if;
         end if;
      end loop;
      if Count = 0 then
         return To_String(Symbols(i)) & "_part";
      else
         return To_String(Symbols(i)) & "_part" & integer_image(Number);
      end if;
   end Portion;


   subtype Hashvals is Integer range 0..26*256+256;
   
   function Getkey(X : in Unbounded_String) return Unbounded_String is
   begin
      return X;
   end Getkey;
   
   function Hashval(X : in Unbounded_String) return Hashvals is
      K : String := To_Lower(To_String(X));
   begin
      case K'Length is
         when 0 => return 0;
         when 1 => return Character'Pos(K(K'First));
         when others => return 
            256*(Character'Pos(K(K'First))-Character'Pos('a'))+
            Character'Pos(K(K'First+1));
      end case;
   exception
      when Constraint_Error =>
         Ada.Text_IO.Put_Line("Invalid identifier: """ &
            To_String(X) & """ is present in the input file.");
         raise;
   end Hashval;
   
   function Is_Equal(Left,Right : in Unbounded_String) return Boolean is
   begin
      return To_Lower(To_String(Left)) = To_Lower(To_String(Right));
   end Is_Equal;
   
   package Token_Hash is new Hash(
      Tableitem => Unbounded_String,
      Key => Unbounded_String, 
      Hashvals => Hashvals, 
      Getkey => Getkey, 
      "=" => Is_Equal, 
      Hashfcn => Hashval); 

   -- this is automatically initialized to null
   -- but do it anyway at start of each file
   Tokens       : String_List.Listptr;
   Nonterminals : String_List.Listptr;
   Methods      : String_List.Listptr;
   Visitors     : String_List.Listptr;
   -- must clear at start of each nonterminal
   Fields_List  : String_List.Listptr;
   Hashed_Tokens : Token_Hash.Hashtable;
   Hashed_Fields : Token_Hash.Hashtable;
   

   procedure Initialize_Nonterminal(X : in out Nonterminal_Record) is
   begin
      X.Length := 1;
      for i in X.Productions'range loop
         X.Productions(i).Length := 0;
      end loop;
   end Initialize_Nonterminal;

   procedure Print_Acceptor_Method(
        Model_File  : in Ada.Text_IO.File_Type;
        Model_Body_File  : in Ada.Text_Io.File_Type;
        Prefix      : in String;      
        Type_Name   : in String) is
   begin
      Ada.Text_IO.Put_Line(Model_File,
         "   procedure Acceptor(This : access " &
         Type_Name & ";");
      Ada.Text_Io.Put_Line(Model_File,
         "      I : access " & Prefix & "_Visitor_Interface.Visit_" &
         Prefix & "_Interface'Class);");
         
      -- body
      Ada.Text_IO.Put_Line(Model_Body_File,
         "   procedure Acceptor(This : access " &
         Type_Name & ";");
      Ada.Text_Io.Put_Line(Model_Body_File,
         "      I : access " & Prefix & "_Visitor_Interface.Visit_" &
         Prefix & "_Interface'Class) is");
      Ada.Text_IO.Put_Line(Model_Body_File,
         "   begin");
      Ada.Text_IO.Put_Line(Model_Body_File,
         "      I.Visit_" & Type_Name & "(This);");
      Ada.Text_IO.Put_Line(Model_Body_File,
         "   end Acceptor;");
      Ada.Text_IO.New_Line(Model_Body_File);
   end Print_Acceptor_Method;

   

   procedure Print_Nonterminal(
         X : in Nonterminal_Record;
         Prefix      : in String;
         DFS_Spec : in Ada.Text_IO.File_Type;
         DFS_Body : in Ada.Text_IO.File_Type;
         Visitor_Interface_Spec : in Ada.Text_IO.File_Type;
         Parser_File : in Ada.Text_Io.File_Type) is
      Model_File : Ada.Text_IO.File_Type;
      Model_Body_File : Ada.Text_Io.File_Type;
      --  print the portion of the interface spec corresponding to this node type
      procedure Print_To_Visitor_Interface_Before(
            File : in Ada.Text_Io.File_Type;
            Dfs_Spec : in Ada.Text_IO.File_Type;
            Dfs_Body : in Ada.Text_IO.File_Type;
            Package_Name : in String;
            Type_Name : in String) is
      begin
         Put_Line(File,"   procedure Before_" & Type_Name & "(");
         Put_Line(File,"      I : access Visit_" & Prefix & "_Interface;");
         Put_Line(File,"      N : access " & Package_Name & "." & Type_Name & "'Class) is null;");
         Put_Line(File,"   procedure Visit_" & Type_Name & "(");
         Put_Line(File,"      I : access Visit_" & Prefix & "_Interface;");
         Put_Line(File,"      N : access " & Package_Name & "." & Type_Name & "'Class) is abstract;");
         Put_Line(File,"   procedure After_" & Type_Name & "(");
         Put_Line(File,"      I : access Visit_" & Prefix & "_Interface;");
         Put_Line(File,"      N : access " & Package_Name & "." & Type_Name & "'Class) is null;");
         New_Line(File);
         ------------------------------------------------------------------------
         -- only needed b/c of GNAT 5.04a bug
         ------------------------------------------------------------------------
         Put_Line(DFS_Spec,"   overriding procedure Before_" & Type_Name & "(");
         Put_Line(DFS_Spec,"      I : access DFS;");
         Put_Line(DFS_Spec,"      N : access " & Package_Name & "." & Type_Name & "'Class);");
         Put_Line(DFS_Spec,"   overriding procedure After_" & Type_Name & "(");
         Put_Line(DFS_Spec,"      I : access DFS;");
         Put_Line(Dfs_Spec,"      N : access " & Package_Name & "." & Type_Name & "'Class);");
         
         Put_Line(DFS_Body,"   procedure Before_" & Type_Name & "(");
         Put_Line(DFS_Body,"      I : access DFS;");
         Put_Line(Dfs_Body,"      N : access " & Package_Name & "." & Type_Name & "'Class) is");
         Put_Line(Dfs_Body,"   begin");
         Put_Line(Dfs_Body,"      null;");
         Put_Line(DFS_Body,"   end Before_" & Type_Name & ";");
         
         Put_Line(DFS_Body,"   procedure After_" & Type_Name & "(");
         Put_Line(DFS_Body,"      I : access DFS;");
         Put_Line(DFS_Body,"      N : access " & Package_Name & "." & Type_Name & "'Class) is");
         Put_Line(Dfs_Body,"   begin");
         Put_Line(Dfs_Body,"      null;");
         Put_Line(DFS_Body,"   end After_" & Type_Name & ";");
         ------------------------------------------------------------------------
         ------------------------------------------------------------------------
         
         Put_Line(DFS_Spec,"   overriding procedure Visit_" & Type_Name & "(");
         Put_Line(DFS_Spec,"      I : access DFS;");
         Put_Line(DFS_Spec,"      N : access " & Package_Name & "." & Type_Name & "'Class);");
         Put_Line(DFS_Body,"   procedure Visit_" & Type_Name & "(");
         Put_Line(DFS_Body,"         I : access DFS;");
         Put_Line(Dfs_Body,"         N : access " & Package_Name & "." & Type_Name & "'Class) is");
         Put_Line(DFS_Body,"      I_Classwide : access DFS'Class := I;");
         Put_Line(Dfs_Body,"   begin");
         Put_Line(Dfs_Body,"      I_Classwide.Before_" & Type_Name & "(N);");
      end Print_To_Visitor_Interface_Before;
      procedure Print_To_Visitor_Interface_After(
            Dfs_Body : in Ada.Text_IO.File_Type;
            Type_Name : in String) is
      begin
         Put_Line(Dfs_Body,"      I_Classwide.After_" & Type_Name & "(N);");
         Put_Line(Dfs_Body,"   end Visit_" & Type_Name & ";");
      end Print_To_Visitor_Interface_After;
      procedure Print_Production(X : in Production_Record) is
      begin
         Ada.Text_IO.Put(Parser_File,"   ");
         for i in 1..X.Length loop
            Ada.Text_IO.Put(Parser_File,
               To_String(X.Symbols(i)) & "_nonterminal ");

            if Token_Hash.Ishashed(Hashed_Tokens,X.Symbols(i)) then
               Ada.Text_IO.Put_Line(Model_File,
                  "      " & Portion(X.Symbols,i,X.Length) & " : " &
                  "Parseable_Token_Ptr;");
               Ada.Text_Io.Put_Line(Dfs_Body,
                  "      I_Classwide.Visit_Parseable_Token(N." &
                  Portion(X.Symbols,i,X.Length) &
                  ");");
            else
               Ada.Text_IO.Put_Line(Model_File,
                  "      " & Portion(X.Symbols,i,X.Length) & " : access " &
                  To_String(X.Symbols(I)) & "_model." &
                  To_String(X.Symbols(i)) & "_nonterminal'Class;");
               Ada.Text_Io.Put_Line(Dfs_Body,
                  "      N." &
                  Portion(X.Symbols,i,X.Length) & 
                  ".Acceptor(I);");
            end if;
         end loop;
         if X.Length = 0 then
            Ada.Text_IO.Put_Line(Model_File,
               "      null;");
         end if;
      end Print_Production;
      -- Either just return Symbols(i), or if duplicated, return
      procedure Print_Action(X : in Production_Record; Symbol : in String) is
      begin
         Ada.Text_IO.Put_Line(Parser_File," {");
         Ada.Text_IO.Put_Line(Parser_File,
            "   $$ := new " & Symbol & ";");
         for i in 1..X.Length loop
            Ada.Text_IO.Put(Parser_File,
               "   " & Symbol & "($$.all)." &
               Portion(X.Symbols,i,X.Length) & " := ");
            -- need appropriate type cast here
            if Token_Hash.Ishashed(Hashed_Tokens,X.Symbols(i)) then
               Ada.Text_IO.Put(Parser_File,
                  "   Parseable_Token_Ptr");
            else
               Ada.Text_IO.Put(Parser_File,
                  "   " & To_String(X.Symbols(i)) & "_Nonterminal_Ptr");
            end if;
            Ada.Text_IO.Put(Parser_File,
               "($" & Integer_Image(i) & ")");
            Ada.Text_IO.Put_Line(Parser_File,";");
         end loop;
         Ada.Text_IO.Put(Parser_File,"   }");
      end Print_Action;
      Fields_Walk : String_List.Listptr;
      use type String_List.Listptr;
   begin
      -- parser file
      Ada.Text_IO.New_Line(Parser_File);
      Ada.Text_IO.Put_Line(Parser_File,
         To_String(X.Symbol) & "_nonterminal : ");
         
      -- model file abstract type if number of productions > 1
      Ada.Text_Io.Create(
         File => Model_File,
         Name => To_Lower(To_String(X.Symbol)) & "_model.ads");
      Autogenerated_Message(Model_File);
      Ada.Text_Io.Create(
         File => Model_Body_File,
         Name => To_Lower(To_String(X.Symbol)) & "_model.adb");
      Autogenerated_Message(Model_Body_File);


      Token_Hash.Clear_Hash(Hashed_Fields);
      String_List.Free(Fields_List);
      -- collects the limited withs
      for I in 1..X.Length loop
         for J in 1..X.Productions(I).Length loop
            if not Token_Hash.Ishashed(Hashed_Fields,X.Productions(I).Symbols(J)) 
                  and not Token_Hash.Ishashed(Hashed_Tokens,X.Productions(I).Symbols(J)) 
                  and To_Lower(To_String(X.Productions(I).Symbols(J))) /=
                     To_Lower(To_String(X.Symbol)) then
               Token_Hash.AddItem(Hashed_Fields,X.Productions(I).Symbols(J));      
               Fields_List := String_List.Cons(X.Productions(I).Symbols(J),
                  Fields_List);
            end if;
         end loop;
      end loop;
      -- prints the limited withs
      Fields_Walk := Fields_List;
      while Fields_Walk /= null loop
         Put_Line(Model_File,
            "limited with " & To_String(String_List.Car(Fields_Walk)) & "_model;");
         Fields_Walk := String_List.Cdr(Fields_Walk);
      end loop;
      Ada.Text_Io.Put_Line(Model_File,
            "with " & Prefix & "_model;");
      Ada.Text_Io.Put_Line(Model_File,
         "use " & Prefix & "_model;");
      Ada.Text_Io.Put_Line(Model_File,
            "with " & Prefix & "_Visitor_Interface;");
         
      -- begin packages
      Ada.Text_Io.Put_Line(Model_File,
         "package " & To_String(X.Symbol) & "_model is"); 
      Ada.Text_Io.Put_Line(Model_Body_File,
         "package body " & To_String(X.Symbol) & "_model is"); 

      --Ada.Text_IO.New_Line(Model_File);
      if X.Length > 1 then
         Ada.Text_IO.Put_Line(Model_File,
            "   type " & To_String(X.Symbol) & 
            "_nonterminal is abstract new Parseable with null record;");
      else
         Ada.Text_IO.Put_Line(Model_File,
            "   type " & To_String(X.Symbol) & 
            "_nonterminal is new Parseable with record");
      end if;
         
      -- loop over number of productions   
      for i in 1..X.Length loop
         -- if there is more than one production,
         -- print another type header
         if X.Length > 1 then
            Ada.Text_IO.Put_Line(Model_File,
               "   type " & To_String(X.Symbol) & 
               "_nonterminal" & integer_image(i) & 
               " is new " & To_String(X.Symbol) & 
               "_nonterminal with record");
            Print_To_Visitor_Interface_Before(
               File => Visitor_Interface_Spec,
               Dfs_Spec => Dfs_Spec,
               DFS_Body => DFS_Body,
               Package_Name => To_String(X.Symbol) & "_Model",
               Type_Name => To_String(X.Symbol) & 
                  "_nonterminal" & integer_image(i));
            Print_Production(X.Productions(i));
            Print_Action(X.Productions(i),
               To_String(X.Symbol) & 
               "_nonterminal" & integer_image(i));
            Print_To_Visitor_Interface_After(
               DFS_Body => DFS_Body,
               Type_Name => To_String(X.Symbol) & 
                  "_nonterminal" & integer_image(i));
         else
            Print_To_Visitor_Interface_Before(
               File => Visitor_Interface_Spec,
               Dfs_Spec => Dfs_Spec,
               DFS_Body => DFS_Body,
               Package_Name => To_String(X.Symbol) & "_Model",
               Type_Name => To_String(X.Symbol) & 
                  "_nonterminal");
            Print_Production(X.Productions(i));
            Print_Action(X.Productions(i),
               To_String(X.Symbol) & 
               "_nonterminal");
            Print_To_Visitor_Interface_After(
               DFS_Body => DFS_Body,
               Type_Name => To_String(X.Symbol) & 
                  "_nonterminal");
         end if;

         if i = X.Length then
            Ada.Text_IO.Put_Line(Parser_File,";");
         else
            Ada.Text_IO.Put_Line(Parser_File,"|");
         end if;
         -- This end record either ends the non-abstract
         -- type (if number of productions is 1), or else
         -- each concrete child
         Ada.Text_IO.Put_Line(Model_File,
            "   end record;"); 
         -- if number > 1 add methods for each numbered type
         -- o/w just for the root type
         if X.Length > 1 then
            Print_Acceptor_Method(Model_File => Model_File,
               Model_Body_File => Model_Body_File,
               Prefix => Prefix,
               Type_Name => To_String(X.Symbol) & 
               "_nonterminal" & Integer_Image(I));
         else
            Print_Acceptor_Method(Model_File => Model_File,
               Model_Body_File => Model_Body_File,
               Prefix => Prefix,
               Type_Name => To_String(X.Symbol) & 
               "_nonterminal");
         end if;
      end loop; 
      Ada.Text_Io.Put_Line(Model_File,
         "end " & To_String(X.Symbol) & "_model;");
      Ada.Text_Io.Put_Line(Model_Body_File,
         "end " & To_String(X.Symbol) & "_model;");
      Ada.Text_IO.Close(Model_File); 
      Ada.Text_IO.Close(Model_Body_File); 

   end Print_Nonterminal;
   
   procedure Verify_Header(
        Header : in String;
        Line   : in String;
        File   : in Ada.Text_IO.File_Type) is
      Word_First, Word_Last : Natural;
      use String_Parsing;
   begin
      String_Parsing.Get_First_Word(
         Line  => Line,
         First => Word_First,
         Last  => Word_Last);
      if Line(Word_First..Word_Last) /= Header then
         Ada.Text_IO.Put_Line("Expected " & Header);
         Ada.Text_IO.Put_Line("Found: """ & Line(Word_First..Word_Last) &
            """ on Line:" &
            Ada.Text_IO.Positive_Count'Image(
               Ada.Text_IO.Line(File)-1));
         raise Bad_File;
      end if;
      
      if Line'Last > Word_Last + 1 and then
         (not Is_Blank_Line(Line(Word_Last+1..Line'Last))) and then
         (not Is_Comment_Line(Line(Word_Last+1..Line'Last))) then
         Ada.Text_IO.Put_Line("Expected " & Header & " on a line by itself");
         Ada.Text_IO.Put_Line("Found: """ & Line&
            """ on Line:" &
            Ada.Text_IO.Positive_Count'Image(
               Ada.Text_IO.Line(File)-1));
         raise Bad_File;
      end if;
   end Verify_Header;
   
   -------------------------------------------------------------
   -- Process_Lexer parses all the way up to (and including) the
   -- section header for the grammar
   -------------------------------------------------------------

   procedure Process_Lexer
     (File   : in Ada.Text_IO.File_Type;
      Prefix : in String)
   is
      Current_Line : Ada.Strings.Unbounded.Unbounded_String;
      Word_First, Word_Last : Natural;
      Second_First, Second_Last : Natural;
      Lexer_File : Ada.Text_IO.File_Type;
      procedure Print_And_Hash(X : in Unbounded_String) is
      begin
         Ada.Text_IO.Put_Line(Lexer_File,"      " & To_String(X) & 
            "_token,");
         Token_Hash.AddItem(Hashed_Tokens,X);
      end Print_And_Hash;
   begin
      -- Clear the token list
      String_List.Free(Tokens);
      Token_Hash.Clear_Hash(Hashed_Tokens);
      
      -- Step 1: Skip comments until macro section header
      -- i.e. loop until neither blank nor comment line
      loop
         Current_Line := To_Unbounded_String(
            File_Helpers.Get_Line(File));
         exit when not(
            String_Parsing.Is_Comment_Line(To_String(Current_Line)) or else
            String_Parsing.Is_Blank_Line(To_String(Current_Line)));
      end loop;
      Verify_Header(Macro_Section_Header,To_String(Current_Line),File);
      
      -- Step 2: Open lexer output file as prefix.l
      begin
         Ada.Text_IO.Create(
            File => Lexer_File,
            Name => To_Lower(Prefix) & ".l",
            Mode => Ada.Text_IO.Out_File);
      exception
         when others => 
            Ada.Text_IO.Put_Line("unable to create " & To_Lower(prefix) & ".l");
            raise;
      end;
      Autogenerated_Message(Lexer_File);
         
      -- Step 3: for each line until we see the Token_Section_Header
      -- copy line
      loop
         Current_Line := To_Unbounded_String(
            File_Helpers.Get_Line(File));
         declare
            Line_String : String := To_String(Current_Line);
         begin
            String_Parsing.Get_First_Word(
               Line  => Line_String,
               First => Word_First,
               Last  => Word_Last);
            exit when Line_String(Word_First..Word_Last) =
               Token_Section_Header;
            Ada.Text_IO.Put_Line(Lexer_File,Line_String);   
         end;
      end loop;
      
      -- Step 3: verify Token_Section_Header and output "%%"
      Verify_Header(Token_Section_Header,To_String(Current_Line),File);
      Ada.Text_IO.Put_Line(Lexer_File,"%%");

      -- Step 4: for every line until the Grammar_Section_Header
      -- read first word (call this ID).  If ID is Ignored_Token_Marker
      -- then simply output the regexp followed by
      -- {Current_Column := Current_Column + YYText'Length;}
      -- o/w, output in {} the above statement, plus return(ID_token);
      -- add ID to Hash table and Linked list of tokens
      loop
         Current_Line := To_Unbounded_String(
            File_Helpers.Get_Line(File));
         declare
            Line_String : String := To_String(Current_Line);
            use String_Parsing;
         begin
            if not(Is_Blank_Line(Line_String) or 
               Is_Comment_Line(Line_String)) then
               String_Parsing.Get_First_Word(
                  Line  => Line_String,
                  First => Word_First,
                  Last  => Word_Last);
               exit when Line_String(Word_First..Word_Last) =
                  Method_List_Marker or
                  Line_String(Word_First..Word_Last) = Grammar_Section_Header;
                  
               -- now that we've read the first word, if
               -- it is ignored, just spit out the regexp
               -- followed by code with no return statement
               -- otherwise, spit out regexp, followed by
               -- code updating column/line and returning token
               -- value
               if Line_String(Word_First..Word_Last) = 
                  Ignored_Token_Marker then
                  -- now get the second word
                  String_Parsing.Get_First_Word(
                     Line  => Line_String(Word_Last+1..Line_String'Last),
                     First => Second_First,
                     Last  => Second_Last);
                  Ada.Text_IO.Put_Line(Lexer_File,
                     Line_String(Second_First..Line_String'Last) & ' ' &
                     "{Current_Column := Current_Column + YYText'Length;}");
               else
                  -- now get the second word
                  String_Parsing.Get_First_Word(
                     Line  => Line_String(Word_Last+1..Line_String'Last),
                     First => Second_First,
                     Last  => Second_Last);
                  -- note the last for the second word is wrong
                  -- since it is allowed to contain spaces
                  -- we just use this to trim leading blanks
                  Ada.Text_IO.Put_Line(Lexer_File,
                     Line_String(Second_First..Line_String'Last) & ' ' &
                     "{Current_Column := Current_Column + YYText'Length; " &
                     "return (" & Line_String(Word_First..Word_Last) &
                     "_token);}");
                  -- make sure we add the token to a list so
                  -- we can create the enumerated type later
                  Tokens := String_List.Cons(
                     To_Unbounded_String(Line_String(Word_First..Word_Last)),
                     Tokens);
               end if;
            end if;
         end;
      end loop;
      
      -- Step 5: output special rules for spaces, tabs and new lines
      -- along with section break
      Ada.Text_IO.Put_Line(Lexer_File,
         "[ \t] {Current_Column := Current_Column + 1;}");
      Ada.Text_IO.Put_Line(Lexer_File,
         "[\n] {Current_Line := Current_Line + 1; Current_Column := 1;}");
      Ada.Text_IO.Put_Line(Lexer_File,"%%");

      -- now output lexer package spec      
      Ada.Text_IO.Put_Line(Lexer_File,
         "package " & Prefix & " is");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   Invalid_Character : exception; -- only can handle ASCII 0..127");
      -- start with a couple of aflex-defined tokens
      Ada.Text_IO.Put_Line(Lexer_File,
         "   type Token is (");
      Ada.Text_IO.Put_Line(Lexer_File,
         "      End_Of_Input,");
      Ada.Text_IO.Put_Line(Lexer_File,
         "      Error,");
      -- print each of the tokens from the list
      -- and add them to the hash table
      String_List.Foreach(Ptr => Tokens, Fnptr => Print_And_Hash'Access);
      -- to allow us to use foreach and have a comma at the end
      -- of every one, simply stick a dummy at the end
      Ada.Text_IO.Put_Line(Lexer_File,
         "      Dummy_Token);");
      Ada.Text_IO.New_Line(Lexer_File);
      -- add the function specs
      Ada.Text_IO.Put_Line(Lexer_File,
         "   procedure Reset(Filename : in String);");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   function Get_Token return Token;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   function Get_Token_String return String;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   function Get_Current_Line return Natural;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   function Get_Current_Column return Natural;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   procedure Close_Files;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "end " & Prefix & ";");
      Ada.Text_IO.New_Line(Lexer_File);

      -- now output lexer package body
      Ada.Text_IO.Put_Line(Lexer_File,
         "with " & Prefix & "_IO;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "package body " & Prefix & " is");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   Current_Line : Natural := 1;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   Current_Column : Natural := 1;");

      -- reset procedure uses AFLEX IO package
      Ada.Text_IO.Put_Line(Lexer_File,
         "   procedure Reset(Filename : in String) is");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   begin");
      Ada.Text_IO.Put_Line(Lexer_File,
         "      Current_Line := 1;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "      Current_Column := 1;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "      " & Prefix & "_IO.Open_Input (Filename);");
      Ada.Text_IO.Put_Line(Lexer_File,
         "      " & Prefix & "_IO.Create_Output;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   end Reset;");
      Ada.Text_IO.New_Line(Lexer_File);
   
      -- close_files procedure uses AFLEX IO package
      Ada.Text_IO.Put_Line(Lexer_File,
         "   procedure Close_Files is");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   begin");
      Ada.Text_IO.Put_Line(Lexer_File,
         "      " & Prefix & "_IO.Close_Input;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "      " & Prefix & "_IO.Close_Output;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   end Close_Files;");
      Ada.Text_IO.New_Line(Lexer_File);

      -- compiler inserts YYLex here
      Ada.Text_IO.Put_Line(Lexer_File,
         "##");
      Ada.Text_IO.New_Line(Lexer_File);

      Ada.Text_IO.Put_Line(Lexer_File,
         "   function Get_Token return Token is");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   begin");
      Ada.Text_IO.Put_Line(Lexer_File,
         "      return YYLex;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   exception when Constraint_Error =>");
      Ada.Text_IO.Put_Line(Lexer_File,
         "      raise Invalid_Character;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   end Get_Token;");
      Ada.Text_IO.New_Line(Lexer_File);

      Ada.Text_IO.Put_Line(Lexer_File,
         "   function Get_Token_String return String is");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   begin");
      Ada.Text_IO.Put_Line(Lexer_File,
         "      return YYText;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   end Get_Token_String;");
      Ada.Text_IO.New_Line(Lexer_File);

      Ada.Text_IO.Put_Line(Lexer_File,
         "   function Get_Current_Line return Natural is");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   begin");
      Ada.Text_IO.Put_Line(Lexer_File,
         "      return Current_Line;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   end Get_Current_Line;");
      Ada.Text_IO.New_Line(Lexer_File);

      Ada.Text_IO.Put_Line(Lexer_File,
         "   function Get_Current_Column return Natural is");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   begin");
      Ada.Text_IO.Put_Line(Lexer_File,
         "      return Current_Column;");
      Ada.Text_IO.Put_Line(Lexer_File,
         "   end Get_Current_Column;");
      Ada.Text_IO.New_Line(Lexer_File);

      Ada.Text_IO.Put_Line(Lexer_File,
         "end " & Prefix & ";");
      -------------------------------------------------------
      -- in 2005 version, we no longer have a method list
      -------------------------------------------------------
      --  Verify_Header(Method_List_Marker,To_String(Current_Line),File);
      Ada.Text_IO.Close(Lexer_File);
   exception 
      when Ada.Text_IO.End_Error =>
         Ada.Text_IO.Put_Line("End of file reached unexpectedly");
         raise Bad_File;
   end Process_Lexer;

      
   -------------------------------------------------------------
   -- Process_Parser parses the grammar section of the file
   -------------------------------------------------------------
   procedure Process_Parser
     (File   : in out Ada.Text_IO.File_Type;
      Prefix : in String)
   is
      Current_Line : Ada.Strings.Unbounded.Unbounded_String;
      Word_First, Word_Last : Natural;
      Second_First, Second_Last : Natural;
      Parser_File : Ada.Text_IO.File_Type;
      Model_File : Ada.Text_Io.File_Type;
      Model_Body_File : Ada.Text_Io.File_Type;
      Visitor_Interface_Spec : Ada.Text_Io.File_Type;
      DFS_Spec, DFS_Body : Ada.Text_IO.File_Type;
      procedure Find_Grammar_Section is
      begin
         loop
            Current_Line := To_Unbounded_String(
               File_Helpers.Get_Line(File));
            declare
               Line_String : String := To_String(Current_Line);
            begin
               String_Parsing.Get_First_Word(
                  Line  => Line_String,
                  First => Word_First,
                  Last  => Word_Last);
               exit when Line_String(Word_First..Word_Last) =
                  Grammar_Section_Header;
            end;
         end loop;
         Verify_Header(Grammar_Section_Header,To_String(Current_Line),File);
      end Find_Grammar_Section;
      
      procedure Skip_Start is
      begin
         loop
            Current_Line := To_Unbounded_String(
               File_Helpers.Get_Line(File));
            declare
               Line_String : String := To_String(Current_Line);
            begin
               String_Parsing.Get_First_Word(
                  Line  => Line_String,
                  First => Word_First,
                  Last  => Word_Last);
               exit when Line_String(Word_First..Word_Last) =
                  Start_Symbol_Marker;
            end;
         end loop;
      end Skip_Start;

      procedure Print_String_List(X : in Unbounded_String) is
      begin
         Ada.Text_IO.Put_Line(Parser_File,"%token " & To_String(X) & 
            "_token");
      end Print_String_List;
      
      procedure Print_Token_Production(X : in Unbounded_String) is
      begin
         Ada.Text_IO.Put_Line(Parser_File,To_String(X) &
            "_nonterminal : " & To_String(X) & 
            "_token");
         Ada.Text_IO.Put_Line(Parser_File,
            "   { Token_String := new String'(Get_Token_String);");
         Ada.Text_IO.Put_Line(Parser_File,
            "     $$ := new Parseable_Token'(" &
            "Get_Current_Line,Get_Current_Column-Token_String.all'Length,");
         Ada.Text_IO.Put_Line(Parser_File,
            "     Token_String," &
            To_String(X) & "_token); } ;");
      end Print_Token_Production;
      
      procedure Print_Nonterminal_Withuse(X : in Unbounded_String) is
      begin
         Ada.Text_IO.Put_Line(Parser_File,
            "with " & To_String(X) & "_model;"
            );
         Ada.Text_IO.Put_Line(Parser_File,
            "use " & To_String(X) & "_model;"
            );
      end Print_Nonterminal_Withuse;

      procedure Nonterminal_Foreshadow(X : in Unbounded_String) is
      begin
         Ada.Text_IO.Put_Line(Parser_File,
            "   type " & To_String(X) & "_Nonterminal_Ptr is access all"
            );
         Ada.Text_IO.Put_Line(Parser_File,
            "      " & To_String(X) & "_model." & To_String(X) & "_Nonterminal'Class;"
            );
      end Nonterminal_Foreshadow;

      procedure Ensure_Unique(X : in Unbounded_String) is
      begin
         if Token_Hash.Ishashed(Hashed_Tokens,X) then
            Ada.Exceptions.Raise_Exception(
               Bad_File'Identity,
               "Token """ & To_String(X) & """ is also a nonterminal.");
         end if;
      end Ensure_Unique;
      
      procedure Print_Interface_Limited_With(X : in Unbounded_String) is
      begin
         Ada.Text_IO.Put_Line(Visitor_Interface_Spec,"limited with " & To_String(X) & "_Model;");
      end Print_Interface_Limited_With;
      procedure DFS_Limited_With(X : in Unbounded_String) is
      begin
         Ada.Text_IO.Put_Line(DFS_Spec,"with " & To_String(X) & "_Model;");
      end DFS_Limited_With;
      -- possibilities for what we're seeking
      type Current_State_Type is (Seek_Nonterminal, Seek_Colon, Seek_End);
      Current_State : Current_State_Type := Seek_Nonterminal;
      
      procedure Accumulate_Nonterminals is
      begin
         -- loop over the rest of the file, accumulating
         -- non-terminal symbols
         while not Ada.Text_IO.End_Of_File(file) loop
            Current_Line := To_Unbounded_String(
               File_Helpers.Get_Line(File));
            declare
               Line_String : String := To_String(Current_Line);
            begin
               if String_Parsing.Is_Comment_Line(Line_String) or else
                  String_Parsing.Is_Blank_Line(Line_String) then
                  null;
               else
                  String_Parsing.Get_First_Word(
                     Line  => Line_String,
                     First => Word_First,
                     Last  => Word_Last);
                  while Word_Last >= Word_First loop
                     case Current_State is
                        when Seek_Nonterminal =>
                           Nonterminals := String_List.Cons(
                              To_Unbounded_String(
                                 Line_String(Word_First..Word_Last)),
                              Nonterminals);
                           Current_State := Seek_Colon;
                        when Seek_Colon =>
                           if Line_String(Word_First..Word_Last) /= ":" then
                              Ada.Text_Io.Put_Line("Expected "":"", Found: """ &
                                 Line_String(Word_First..Word_Last) & """ on Line:" &
                                 Ada.Text_IO.Positive_Count'Image(
                                    Ada.Text_IO.Line(File)-1));
                              raise Bad_File;
                           end if;
                           Current_State := Seek_End;
                        when Seek_End =>
                           if Line_String(Word_First..Word_Last) = ";" then
                              Current_State := Seek_Nonterminal;
                           end if;
                     end case;
                     String_Parsing.Get_First_Word(
                        Line  => Line_String(Word_Last+1..Line_String'Last),
                        First => Word_First,
                        Last  => Word_Last);
                  end loop;
               end if;
            end;
         end loop;
      end Accumulate_Nonterminals;

      -- this procedure contains a huge amount of 
      -- identical code as accumulate_nonterminals, as it
      -- needs to walk the file in the same way
      --
      -- the important call is Print_Nonterminal
      procedure Process_Nonterminals is
         Nonterminal : Nonterminal_Record;
      begin
         -- loop over the rest of the file, processing
         -- non-terminal symbols
         while not Ada.Text_IO.End_Of_File(file) loop
            Current_Line := To_Unbounded_String(
               File_Helpers.Get_Line(File));
            declare
               Line_String : String := To_String(Current_Line);
            begin
               if String_Parsing.Is_Comment_Line(Line_String) or else
                  String_Parsing.Is_Blank_Line(Line_String) then
                  null;
               else
                  String_Parsing.Get_First_Word(
                     Line  => Line_String,
                     First => Word_First,
                     Last  => Word_Last);
                  while Word_Last >= Word_First loop
                     case Current_State is
                        when Seek_Nonterminal =>
                           Nonterminal.Symbol :=
                              To_Unbounded_String(
                                 Line_String(Word_First..Word_Last));
                           Initialize_Nonterminal(Nonterminal);
                           Current_State := Seek_Colon;
                        when Seek_Colon =>
                           if Line_String(Word_First..Word_Last) /= ":" then
                              Ada.Text_Io.Put_Line("Expected "":"", Found: """ &
                                 Line_String(Word_First..Word_Last) &
                                 """ on Line:" &
                                 Ada.Text_IO.Positive_Count'Image(
                                    Ada.Text_IO.Line(File)-1));
                              raise Bad_File;
                           end if;
                           Current_State := Seek_End;
                        when Seek_End =>
                           if Line_String(Word_First..Word_Last) = ";" then
                              Current_State := Seek_Nonterminal;
                              Print_Nonterminal(
                                 X           => Nonterminal,
                                 Prefix      => Prefix,
                                 Dfs_Spec    => Dfs_Spec,
                                 DFS_Body    => DFS_Body,
                                 Visitor_Interface_Spec => Visitor_Interface_Spec,
                                 Parser_File => Parser_File);
                           elsif Line_String(Word_First..Word_Last) = "|" then
                              Nonterminal.Length := Nonterminal.Length + 1;
                           else
                              Nonterminal.Productions(Nonterminal.Length).Length := 
                                 Nonterminal.Productions(Nonterminal.Length).Length + 1;
                              Nonterminal.Productions(Nonterminal.Length).Symbols(
                                 Nonterminal.Productions(Nonterminal.Length).Length) :=
                                 To_Unbounded_String(
                                    Line_String(Word_First..Word_Last));
                           end if;
                     end case;
                     String_Parsing.Get_First_Word(
                        Line  => Line_String(Word_Last+1..Line_String'Last),
                        First => Word_First,
                        Last  => Word_Last);
                  end loop;
               end if;
            end;
         end loop;
      end Process_Nonterminals;
   begin
      String_List.Free(Nonterminals);
      String_List.Free(Methods);
      String_List.Free(Visitors);
      -- Step 1: Open parser output file as prefix.y
      begin
         Ada.Text_IO.Create(
            File => Parser_File,
            Name => To_Lower(Prefix) & ".y",
            Mode => Ada.Text_IO.Out_File);
      exception
         when others => 
            Ada.Text_IO.Put_Line("unable to create " & To_Lower(prefix) & ".y");
            raise;
      end;
      
      begin
         Ada.Text_IO.Create(
            File => Model_File,
            Name => To_Lower(Prefix) & "_model.ads",
            Mode => Ada.Text_Io.Out_File);
         Autogenerated_Message(Model_File);
      exception
         when others => 
            Ada.Text_IO.Put_Line("unable to create " & To_Lower(prefix) & "_model.ads");
            Ada.Text_IO.Put_Line("continuing...");
      end;

      begin
         Ada.Text_IO.Create(
            File => Model_Body_File,
            Name => To_Lower(Prefix) & "_model.adb",
            Mode => Ada.Text_Io.Out_File);
         Autogenerated_Message(Model_Body_File);
      exception
         when others => 
            Ada.Text_IO.Put_Line("unable to create " & To_Lower(prefix) & "_model.adb");
            Ada.Text_IO.Put_Line("continuing...");
      end;

      begin
         Ada.Text_IO.Create(
            File => Visitor_Interface_Spec,
            Name => To_Lower(Prefix) & "_visitor_interface.ads",
            Mode => Ada.Text_Io.Out_File);
         Autogenerated_Message(Visitor_Interface_Spec);
      exception
         when others => 
            Ada.Text_IO.Put_Line("unable to create " & To_Lower(prefix) & "_visitor_interface.ads");
            Ada.Text_IO.Put_Line("continuing...");
      end;

      begin
         Ada.Text_IO.Create(
            File => DFS_Spec,
            Name => To_Lower(Prefix) & "_dfs.ads",
            Mode => Ada.Text_Io.Out_File);
         Autogenerated_Message(DFS_Spec);
      exception
         when others => 
            Ada.Text_IO.Put_Line("unable to create " & To_Lower(prefix) & "_dfs.ads");
            Ada.Text_IO.Put_Line("continuing...");
      end;

      begin
         Ada.Text_IO.Create(
            File => DFS_Body,
            Name => To_Lower(Prefix) & "_dfs.adb",
            Mode => Ada.Text_Io.Out_File);
         Autogenerated_Message(DFS_Body);
      exception
         when others => 
            Ada.Text_IO.Put_Line("unable to create " & To_Lower(prefix) & "_dfs.adb");
            Ada.Text_IO.Put_Line("continuing...");
      end;

      Ada.Text_IO.Put_Line(Model_Body_File,
         "package body " & Prefix & "_Model is");
      Ada.Text_IO.Put_Line(Model_Body_File,
         "   procedure Acceptor(This : access Parseable_Token;");
      Ada.Text_Io.Put_Line(Model_Body_File,
         "      I : access " & Prefix & "_Visitor_Interface.Visit_" & Prefix & "_Interface'Class) is");
      Ada.Text_Io.Put_Line(Model_Body_File,
         "   begin");
      Ada.Text_Io.Put_Line(Model_Body_File,
         "      I.Visit_Parseable_Token(This);");
      Ada.Text_Io.Put_Line(Model_Body_File,
         "   end Acceptor;");


      Autogenerated_Message(Parser_File);
      Ada.Text_IO.New_Line(Parser_File);
      Ada.Text_IO.Put_Line(Parser_File,
         "{");
      Ada.Text_IO.Put_Line(Parser_File,
         "with " & Prefix & "_Model;");
      Ada.Text_IO.Put_Line(Parser_File,
         "use " & Prefix & "_Model;");
      Ada.Text_IO.New_Line(Parser_File);

      Ada.Text_IO.Put_Line(Parser_File,
         "package " & Prefix & "_tokens is");
      Ada.Text_IO.Put_Line(Parser_File,
         "}");
      Ada.Text_IO.New_Line(Parser_File);

      -- print each of the tokens from the list
      -- with %token 
      String_List.Foreach(
         Ptr   => Tokens, 
         Fnptr => Print_String_List'Access);

      -- read up to start symbol marker
      loop
         Current_Line := To_Unbounded_String(
            File_Helpers.Get_Line(File));
         exit when not(
            String_Parsing.Is_Comment_Line(To_String(Current_Line)) or else
            String_Parsing.Is_Blank_Line(To_String(Current_Line)));
      end loop;

      declare
         Line_String : String := To_String(Current_Line);
      begin
         String_Parsing.Get_First_Word(
            Line  => Line_String,
            First => Word_First,
            Last  => Word_Last);
         if To_Lower(Line_String(Word_First..Word_Last)) /=
            Start_Symbol_Marker then
            Ada.Text_IO.Put_Line("expected: " & Start_Symbol_Marker);
            Ada.Text_IO.Put_Line("found: """ & Line_String &
               """ on Line:" &
               Ada.Text_IO.Positive_Count'Image(
                  Ada.Text_IO.Line(File)-1));
            raise Bad_File;
         end if;
         String_Parsing.Get_First_Word(
            Line  => Line_String(Word_Last+1..Line_String'Last),
            First => Second_First,
            Last  => Second_Last);
         Ada.Text_IO.New_Line(Parser_File);
         Ada.Text_IO.Put_Line(Parser_File,"%start adagoop_first");

         Ada.Text_IO.Put_Line(Parser_File,
            "{");
         Ada.Text_IO.Put_Line(Parser_File,
            "subtype YYSTYPE is " & Prefix & "_Model.Parseable_Ptr;");
         Ada.Text_IO.Put_Line(Parser_File,
            "}");
         Ada.Text_IO.New_Line(Parser_File);
         Ada.Text_IO.Put_Line(Parser_File,"%%");
         Ada.Text_IO.New_Line(Parser_File);

         -- print each of the tokens from the list
         -- as a production 
         String_List.Foreach(
            Ptr   => Tokens, 
            Fnptr => Print_Token_Production'Access);

         Ada.Text_IO.New_Line(Parser_File);
         Ada.Text_IO.Put_Line(Parser_File,"adagoop_first : " &
            Line_String(Second_First..Second_Last) & "_nonterminal");
         Ada.Text_IO.Put_Line(Parser_File,"{ Parse_Tree := $1; } ; ");
      end;
      
      -- print model file header
      Ada.Text_IO.Put_Line(Model_File,
         "with " & Prefix & ";");
      Ada.Text_IO.Put_Line(Model_File,
         "with " & Prefix & "_Visitor_Interface;");
      Ada.Text_IO.Put_Line(Model_File,"package " &
         Prefix & "_Model is");
      Ada.Text_IO.Put_Line(Model_File,
         "   type String_Ptr is access all String;");
      Ada.Text_IO.New_Line(Model_File);
      Ada.Text_IO.Put_Line(Model_File,
         "   type Parseable is abstract tagged null record;");
      ------------------------------------------------------------------
      -- don't do these anymore in AdaGOOP 2005
      -- Print_Visitor_Method(Model_File,Model_Body_File,"Parseable",true);
      ------------------------------------------------------------------
      Ada.Text_IO.Put_Line(Model_File,
         "   type Parseable_Ptr is access all Parseable'Class;");
      Ada.Text_IO.Put_Line(Model_File,
         "   procedure Acceptor(This : access Parseable;");
      Ada.Text_Io.Put_Line(Model_File,
         "      I : access " & Prefix & "_Visitor_Interface.Visit_" & Prefix & "_Interface'Class)" &
          " is abstract;");
      Ada.Text_IO.New_Line(Model_File);
      Ada.Text_IO.Put_Line(Model_File,
         "   type Parseable_Token is new Parseable with record");
      Ada.Text_IO.Put_Line(Model_File,
         "      Line         : Natural;");
      Ada.Text_IO.Put_Line(Model_File,
         "      Column       : Natural;");
      Ada.Text_IO.Put_Line(Model_File,
         "      Token_String : String_Ptr;");
      Ada.Text_IO.Put_Line(Model_File,
         "      Token_Type   : " & Prefix & ".Token;");
      Ada.Text_IO.Put_Line(Model_File,
         "   end record;");

      ------------------------------------------------------------------
      Ada.Text_IO.Put_Line(Model_File,
         "   type Parseable_Token_Ptr is access all Parseable_Token'Class;");
      Ada.Text_IO.New_Line(Model_File);
      Ada.Text_IO.Put_Line(Model_File,
         "   procedure Acceptor(This : access Parseable_Token;");
      Ada.Text_Io.Put_Line(Model_File,
         "      I : access " & Prefix & "_Visitor_Interface.Visit_" & Prefix & "_Interface'Class);");
       
      
      -- accumulate nonterminals and print foreshadowing to 
      -- model file
      Current_State := Seek_Nonterminal;
      Accumulate_Nonterminals;      
      -- make sure no nonterminals match a token
      String_List.Foreach(
         Ptr   => Nonterminals,
         Fnptr => Ensure_Unique'Access);
      
      String_List.Foreach(
         Ptr   => Nonterminals,
         Fnptr => Print_Interface_Limited_With'Access);
      String_List.Foreach(
         Ptr   => Nonterminals,
         Fnptr => DFS_Limited_With'Access);

      Ada.Text_IO.Put_Line(Visitor_Interface_Spec,"limited with " &
         Prefix & "_Model;");
      Ada.Text_IO.Put_Line(DFS_Spec,"limited with " &
         Prefix & "_Model;");

      Ada.Text_IO.Put_Line(Visitor_Interface_Spec,"package " &
         Prefix & "_Visitor_Interface is");
      Ada.Text_Io.Put_Line(Visitor_Interface_Spec,
         "   type Visit_" & Prefix & "_Interface is interface;");
      Ada.Text_Io.New_Line(Visitor_Interface_Spec);
      Ada.Text_Io.Put_Line(Visitor_Interface_Spec,
         "   procedure Visit_Parseable_Token(");
      Ada.Text_Io.Put_Line(Visitor_Interface_Spec,
         "      I : access Visit_" & Prefix & "_Interface;");
      Ada.Text_Io.Put_Line(Visitor_Interface_Spec,
         "      T : access " & Prefix & "_Model.Parseable_Token'Class) is null;");
      Ada.Text_Io.New_Line(Visitor_Interface_Spec);


      Ada.Text_IO.Put_Line(DFS_Spec,"with " &
         Prefix & "_Visitor_Interface;");
      Ada.Text_IO.Put_Line(DFS_Spec,"use " &
         Prefix & "_Visitor_Interface;");
      Ada.Text_IO.Put_Line(DFS_Spec,"package " &
         Prefix & "_DFS is");
      Ada.Text_Io.Put_Line(DFS_Spec,
         "   type Root is tagged null record;");
      Ada.Text_Io.Put_Line(DFS_Spec,
         "   type DFS is new Root and ");
      Ada.Text_Io.Put_Line(Dfs_Spec,
         "      " & 
         Prefix & "_Visitor_Interface.Visit_" & Prefix & "_Interface with null record;");

      Ada.Text_IO.Put_Line(DFS_Body,"package body " &
         Prefix & "_DFS is");

      -- go back to the beginning for second pass
      Ada.Text_IO.Reset(File);
      Find_Grammar_Section;
      Skip_Start;
      
      -- process second time, outputting type to model
      -- and grammar to parser
      Current_State := Seek_Nonterminal;
      Process_Nonterminals;
      
      -- print end of model file      
      Ada.Text_IO.Put_Line(Model_File,"end " &
         Prefix & "_Model;");
         
      -- output the package spec and body for the parser
      Ada.Text_IO.New_Line(Parser_File);
      Ada.Text_IO.Put_Line(Parser_File,"%%");
      Ada.Text_IO.New_Line(Parser_File);
      Ada.Text_IO.Put_Line(Parser_File,"with " & Prefix & "_Model;");
      Ada.Text_IO.Put_Line(Parser_File,"use " & prefix & "_Model;");
      Ada.Text_IO.Put_Line(Parser_File,"package " & Prefix & 
         "_Parser is"); 
      Ada.Text_IO.Put_Line(Parser_File, 
         "   procedure Run(Filename : in String);"); 
      Ada.Text_IO.Put_Line(Parser_File, 
         "   function Get_Parse_Tree return Parseable_Ptr;"); 
      Ada.Text_IO.Put_Line(Parser_File,"end " &
         Prefix & "_Parser;");
      Ada.Text_IO.New_Line(Parser_File);
      Ada.Text_IO.Put_Line(Parser_File,"with Text_IO;"); 
      Ada.Text_IO.Put_Line(Parser_File,"use Text_IO;");
      Ada.Text_IO.Put_Line(Parser_File,"with " & Prefix & ";"); 
      Ada.Text_IO.Put_Line(Parser_File,"use " & Prefix & ";");
      Ada.Text_IO.Put_Line(Parser_File,"with " & Prefix & "_Goto;");
      Ada.Text_IO.Put_Line(Parser_File,"use " & prefix & "_Goto;");
      Ada.Text_IO.Put_Line(Parser_File,"with " & Prefix & "_Shift_Reduce;");
      Ada.Text_IO.Put_Line(Parser_File,"use " & prefix & "_Shift_Reduce;");
      Ada.Text_IO.Put_Line(Parser_File,"with " & Prefix & "_Tokens;");
      Ada.Text_IO.Put_Line(Parser_File,"use " & prefix & "_Tokens;");
      -- add "with" and "use" for each nonterminal package
      String_List.Foreach(
         Ptr   => Nonterminals, 
         Fnptr => Print_Nonterminal_Withuse'Access);

      Ada.Text_IO.Put_Line(Parser_File,"package body " & Prefix & 
         "_Parser is"); 
      Ada.Text_IO.Put_Line(Parser_File, 
         "   package Language_YY2_Lexical_Analyzer renames " & Prefix &
         ";"); 
      Ada.Text_IO.Put_Line(Parser_File, 
         "   use Language_YY2_Lexical_Analyzer;");
      Ada.Text_IO.New_Line(Parser_File);
      String_List.Foreach(
         Ptr   => Nonterminals, 
         Fnptr => Nonterminal_Foreshadow'Access);
      Ada.Text_IO.Put_Line(Parser_File,
         "   Parse_Tree : Parseable_Ptr;"); 
      Ada.Text_IO.Put_Line(Parser_File,
         "   Token_String : String_Ptr;"); 
      Ada.Text_IO.New_Line(Parser_File);
      Ada.Text_IO.Put_Line(Parser_File, 
         "   procedure YYError(S : in String := ""Syntax Error"") is"); 
      Ada.Text_IO.Put_Line(Parser_File, 
         "   begin"); 
      Ada.Text_IO.Put_Line(Parser_File, 
         "      Put_Line(S & "" on line "" & " &
         "         integer'image(" & Prefix & ".Get_Current_Line));");     
      Ada.Text_IO.Put_Line(Parser_File, 
         "   end YYError;"); 
      Ada.Text_IO.New_Line(Parser_File);
      Ada.Text_IO.Put_Line(Parser_File, 
         "##%procedure_parse");
      Ada.Text_IO.New_Line(Parser_File);
      Ada.Text_IO.Put_Line(Parser_File, 
         "   procedure Run(Filename : in String) is"); 
      Ada.Text_IO.Put_Line(Parser_File, 
         "   begin"); 
      Ada.Text_IO.Put_Line(Parser_File,
         "      " & Prefix & ".Reset(Filename);");
      Ada.Text_IO.Put_Line(Parser_File,
         "      begin");
      Ada.Text_IO.Put_Line(Parser_File,
         "         YYParse;");
      Ada.Text_IO.Put_Line(Parser_File,
         "      exception");
      Ada.Text_IO.Put_Line(Parser_File,
         "         when Invalid_Character => YYError(""Invalid Character"");");
      Ada.Text_IO.Put_Line(Parser_File,
         "         when others => YYError;");
      Ada.Text_IO.Put_Line(Parser_File,
         "      end;");
      Ada.Text_IO.Put_Line(Parser_File,
         "      " & Prefix & ".Close_Files;");
      Ada.Text_IO.Put_Line(Parser_File, 
         "   end Run;"); 
      Ada.Text_IO.New_Line(Parser_File);
      Ada.Text_IO.Put_Line(Parser_File, 
         "   function Get_Parse_Tree return Parseable_Ptr is"); 
      Ada.Text_IO.Put_Line(Parser_File, 
         "   begin"); 
      Ada.Text_IO.Put_Line(Parser_File, 
         "      return Parse_Tree;"); 
      Ada.Text_IO.Put_Line(Parser_File, 
         "   end Get_Parse_Tree;"); 
      Ada.Text_IO.Put_Line(Parser_File,"end " &
         Prefix & "_Parser;");
      Ada.Text_IO.Put_Line(Visitor_Interface_Spec,"end " &
         Prefix & "_Visitor_Interface;");
      Ada.Text_IO.Put_Line(DFS_Spec,"end " &
         Prefix & "_DFS;");
      Ada.Text_IO.Put_Line(DFS_Body,"end " &
         Prefix & "_DFS;");
      Ada.Text_IO.Put_Line(Model_Body_File,"end " &
         Prefix & "_Model;");

      Ada.Text_IO.Close(Parser_File);
      Ada.Text_IO.Close(Model_File);
      Ada.Text_IO.Close(Model_Body_File);
      Ada.Text_IO.Close(Visitor_Interface_Spec);
      Ada.Text_IO.Close(DFS_Spec);
      Ada.Text_IO.Close(DFS_Body);
   exception 
      when Ada.Text_IO.End_Error =>
         Ada.Text_IO.Put_Line("End of file reached unexpectedly");
         raise Bad_File;
   end Process_Parser;
   
   procedure Process_File
     (File   : in out Ada.Text_IO.File_Type;
      Prefix : in String)
   is
   begin
      Process_Lexer(File,Prefix);
      Unbounded_Prefix := To_Unbounded_String(Prefix);
      Process_Parser(File,Prefix);
   end Process_File;

end Generate;

