-- $Header: /dc/uc/self/arcadia/ayacc/src/RCS/parse_template_file.a,v 1.1 1993/01/15 23:39:54 self Exp self $

-- Copyright (c) 1990 Regents of the University of California.
-- All rights reserved.
--
--    The primary authors of ayacc were David Taback and Deepak Tolani.
--    Enhancements were made by Ronald J. Schmalz.
--
--    Send requests for ayacc information to ayacc-info@ics.uci.edu
--    Send bug reports for ayacc to ayacc-bugs@ics.uci.edu
--
-- Redistribution and use in source and binary forms are permitted
-- provided that the above copyright notice and this paragraph are
-- duplicated in all such forms and that any documentation,
-- advertising materials, and other materials related to such
-- distribution and use acknowledge that the software was developed
-- by the University of California, Irvine.  The name of the
-- University may not be used to endorse or promote products derived
-- from this software without specific prior written permission.
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
-- IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-- WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.

-- Module       : parse_template_file.ada
-- Component of : ayacc
-- Version      : 1.2
-- Date         : 11/21/86  12:33:32
-- SCCS File    : disk21~/rschm/hasee/sccs/ayacc/sccs/sxparse_template_file.ada

--**
--  6-Aug-2008 GdM: Added "at line ..." to Syntax Error
--        2006 GdM: Added ref to YY_Sizes package (no more hardcoding of sizes)

--**

-- $Header: /dc/uc/self/arcadia/ayacc/src/RCS/parse_template_file.a,v 1.1 1993/01/15 23:39:54 self Exp self $
-- $Log: parse_template_file.a,v $
-- Revision 1.1  1993/01/15  23:39:54  self
-- Initial revision
--
--Revision 1.1  88/08/08  14:20:23  arcadia
--Initial revision
--
-- Revision 0.1  86/04/01  15:09:47  ada
--  This version fixes some minor bugs with empty grammars
--  and $$ expansion. It also uses vads5.1b enhancements
--  such as pragma inline.
--
--
-- Revision 0.0  86/02/19  18:40:09  ada
--
-- These files comprise the initial version of Ayacc
-- designed and implemented by David Taback and Deepak Tolani.
-- Ayacc has been compiled and tested under the Verdix Ada compiler
-- version 4.06 on a vax 11/750 running Unix 4.2BSD.
--

with Ada.Strings.Fixed;
with Ada.Directories;

with Options;
with Ayacc_File_Names;
package body Parse_Template_File is

   use Ada.Text_IO;

   -- SCCS_ID : constant String := "@(#) parse_template_file.ada, Version 1.2";
   -- Rcs_ID : constant String := "$Header: /dc/uc/self/arcadia/ayacc/src/RCS/parse_template_file.a,v 1.1 1993/01/15 23:39:54 self Exp self $";

   DECL_CODE_FILENAME    : constant String := "code.miq";
   INIT_CODE_FILENAME    : constant String := "init.miq";

   function Get_Filename (Code : in Code_Filename) return String is
   begin
      case Code is
         when DECL_CODE =>
            return DECL_CODE_FILENAME;

         when INIT_CODE =>
            return INIT_CODE_FILENAME;

      end case;
   end Get_Filename;

   procedure Include_File (Outfile : in File_Type;
                           Filename : in String) is
      Input : File_Type;
   begin
      if not Ada.Directories.Exists (Filename) then
         return;
      end if;
      Open (Input, Ada.Text_IO.In_File, Filename);
      while not End_Of_File (Input) loop
         Put_Line (Outfile, Get_Line (Input));
      end loop;
      Close (Input);
   end Include_File;

   procedure Write_Indented (Outfile : in Ada.Text_IO.File_Type;
                             Line    : in String) is
      First : constant Positive_Count := Ada.Text_IO.Col (Outfile);
      Start : constant Natural := Ada.Strings.Fixed.Index (Line, "(");
      Pos   : Natural := Line'First;
      Sep   : Natural;
   begin
      while Pos <= Line'Last loop
         Sep := Ada.Strings.Fixed.Index (Line, ";", Pos);
         if Sep > 0 then
            Put_Line (Outfile, Line (Pos .. Sep));
            Pos := Sep + 1;
            Set_Col (Outfile, First + Positive_Count (Start - Line'First));
         else
            Put (Outfile, Line (Pos .. Line'Last));
            return;
         end if;
      end loop;
   end Write_Indented;

   procedure Write_Line (Outfile : in File_Type;
                         Line    : in String) is
      Start : Natural := Line'First;
      Pos   : Natural;
   begin
      while Start <= Line'Last loop
         Pos := Ada.Strings.Fixed.Index (Line, "${", Start);
         if Pos = 0 then
            Put_Line (Outfile, Line (Start .. Line'Last));
            exit;
         elsif Line (Pos .. Pos + 7) = "${YYLEX}" then
            declare
               Name : constant String := Ayacc_File_Names.Lex_Function_Name;
            begin
               Put (Outfile, Line (Start .. Pos - 1));
               if Name'Length > 0 then
                  Put (Outfile, Name);
               else
                  Put (Outfile, "YYLex");
               end if;
               Start := Pos + 8;

            end;
         elsif Line (Pos .. Pos + 9) = "${YYPARSE}" then
            Put (Outfile, Line (Start .. Pos - 1));
            Put (Outfile, Ayacc_File_Names.Get_Parse_Name);
            Start := Pos + 10;

         elsif Line (Pos .. Pos + 6) = "${NAME}" then
            Put (Outfile, Line (Start .. Pos - 1));
            Put (Outfile, Options.Ayacc_Stack_Size);
            Start := Pos + 7;

         elsif Line (Pos .. Pos + 14) = "${YYPARSEPARAM}" then
            Put (Outfile, Line (Start .. Pos - 1));
            Write_Indented (Outfile, Ayacc_File_Names.get_Parse_Params);
            Start := Pos + 15;

         elsif Line (Pos .. Pos + 13) = "${YYSTACKSIZE}" then
            begin
               Put (Outfile, Line (Start .. Pos - 1));
               Put (Outfile, Options.Ayacc_Stack_Size);
               Start := Pos + 14;
            end;
         else
            Put_Line (Outfile, Line (Start .. Line'Last));
            exit;
         end if;
         if Start > Line'Last then
            New_Line (Outfile);
         end if;
      end loop;
   end Write_Line;

   procedure Template_Writer (Outfile  : in File_Type) is

      type Section_Type is (S_COMMON,
                            S_IF_DEBUG,
                            S_IF_ERROR,
                            S_IF_PRIVATE,
                            S_IF_REENTRANT,
                            S_IF_YYERROK,
                            S_IF_YYCLEARIN);
      Current    : Section_Type := S_COMMON;
      Is_Visible : Boolean := True;
      Invert     : Boolean := False;
      Continue   : Boolean := True;
   begin
      while Continue and then Has_Line loop
         declare
            Line : constant String := Get_Line;
         begin
            if Line'Length = 0 then
               if Is_Visible then
                  New_Line (Outfile);
               end if;
            elsif Line (Line'First) = '%' then
               if Line = "%if debug" then
                  Current := S_IF_DEBUG;
               elsif Line = "%if error" then
                  Current := S_IF_ERROR;
               elsif Line = "%if private" then
                  Current := S_IF_PRIVATE;
               elsif Line = "%if reentrant" then
                  Current := S_IF_REENTRANT;
               elsif Line = "%if yyerrok" then
                  Current := S_IF_YYERROK;
               elsif Line = "%if yyclearin" then
                  Current := S_IF_YYCLEARIN;
               elsif Line = "%end" then
                  Current := S_COMMON;
                  Invert := False;
               elsif Line = "%else" then
                  Invert := True;
               elsif Line'Length > 3 and then Line (Line'First + 1) = '%' then
                  Continue := False;
                  return;
               elsif Line = "%yydecl" then
                  if Is_Visible then
                     Include_File (Outfile, DECL_CODE_FILENAME);
                  end if;
               elsif Line = "%yyinit" then
                  if Is_Visible then
                     Include_File (Outfile, INIT_CODE_FILENAME);
                  end if;
               else
                  --  Very crude error report when the template % line is invalid.
                  --  This could happen only during development when templates
                  --  are modified.
                  raise Program_Error with "Invalid template '%' rule: " & Line;
               end if;
               Is_Visible := (Current = S_COMMON)
                 or else (Current = S_IF_DEBUG and then Options.Debug)
                 or else (Current = S_IF_PRIVATE and then Options.Package_Private)
                 or else (Current = S_IF_REENTRANT and then Options.Reentrant)
                 or else (Current = S_IF_YYERROK and then not Options.Skip_Yyerrok)
                 or else (Current = S_IF_YYCLEARIN and then not Options.Skip_Yyclearin)
                 or else (Current = S_IF_ERROR and then Options.Error_Recovery_Extension);
               if Invert then
                  Is_Visible := not Is_Visible;
               end if;

            elsif Is_Visible then
               Write_Line (Outfile, Line);
            end if;
         end;
      end loop;
   end Template_Writer;

   procedure Write_Template (Outfile  : in File_Type;
                             Lines    : in Content_Array;
                             Position : in out Positive) is
      function Has_Line return Boolean is
      begin
         return Position <= Lines'Last;
      end Has_Line;

      function Get_Line return String is
      begin
         Position := Position + 1;
         return Lines (Position - 1).all;
      end Get_Line;

      procedure Write is new Template_Writer (Has_Line, Get_Line);

   begin
      Write (Outfile);
   end Write_Template;

   procedure Cleanup is
   begin
      if Ada.Directories.Exists (DECL_CODE_FILENAME) then
         Ada.Directories.Delete_File (DECL_CODE_FILENAME);
      end if;
      if Ada.Directories.Exists (INIT_CODE_FILENAME) then
         Ada.Directories.Delete_File (INIT_CODE_FILENAME);
      end if;
   end Cleanup;

end Parse_Template_File;
