-------------------------------------------------------------------------------
-- (C) Altran Praxis Limited
-------------------------------------------------------------------------------
--
-- The SPARK toolset is free software; you can redistribute it and/or modify it
-- under terms of the GNU General Public License as published by the Free
-- Software Foundation; either version 3, or (at your option) any later
-- version. The SPARK toolset is distributed in the hope that it will be
-- useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-- Public License for more details. You should have received a copy of the GNU
-- General Public License distributed with the SPARK toolset; see file
-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy of
-- the license.
--
--=============================================================================

with Command_Line_Options;
with SPARK_Ada_Integer_Text_IO;
with SPARK.Ada.Command_Line;
with SPARK.Ada.Strings.Unbounded;
with SPARK.Ada.Text_IO.Unbounded_String;
with Sparklalr_Level;

use type SPARK.Ada.Text_IO.Exception_T;

package body Symbols_Dump
--# own State is Nnon_Terms,
--#              Nterms,
--#              Nterm_Set,
--#              Term_Set;
is

   type Nterm_Set_T is array (Sparklalr_Common.Non_Term_Range) of Sparklalr_Common.Id_Name;
   type Term_Set_T is array (Sparklalr_Common.Term_Range) of Sparklalr_Common.Id_Name;

   Nterm_Set : Nterm_Set_T;
   Term_Set  : Term_Set_T;

   Nterms, Nnon_Terms : Integer;

   procedure Initialise
   --# global out Nnon_Terms;
   --#        out Nterms;
   --#        out Nterm_Set;
   --#        out Term_Set;
   --# derives Nnon_Terms,
   --#         Nterms,
   --#         Nterm_Set,
   --#         Term_Set   from ;
   is
   begin
      Nterms     := -1;
      Nnon_Terms := 0;
      Nterm_Set  := Nterm_Set_T'(others => Sparklalr_Common.Id_Name'(others => ' '));
      Term_Set   := Term_Set_T'(others => Sparklalr_Common.Id_Name'(others => ' '));
   end Initialise;

   procedure Define
     (Tnt           : in     Boolean;
      F             : in out SPARK.Ada.Text_IO.File_Type;
      Gram_Rules    : in     Boolean;
      Token         : in     Sparklalr_Common.Id_Name;
      Col           : in     Sparklalr_Error.Err_Col_T;
      Result_Define :    out Integer)
   -- DEFINES A NEW (NON) TERMINAL
   --# global in out Nnon_Terms;
   --#        in out Nterms;
   --#        in out Nterm_Set;
   --#        in out Sparklalr_Error.State;
   --#        in out Sparklalr_Level.State;
   --#        in out Term_Set;
   --# derives F                     from *,
   --#                                    Nnon_Terms,
   --#                                    Tnt &
   --#         Nnon_Terms,
   --#         Nterms                from *,
   --#                                    Tnt &
   --#         Nterm_Set             from *,
   --#                                    Nnon_Terms,
   --#                                    Tnt,
   --#                                    Token &
   --#         Result_Define         from Nnon_Terms,
   --#                                    Nterms,
   --#                                    Tnt &
   --#         Sparklalr_Error.State from *,
   --#                                    Col,
   --#                                    Nnon_Terms,
   --#                                    Nterms,
   --#                                    Tnt &
   --#         Sparklalr_Level.State from *,
   --#                                    Gram_Rules,
   --#                                    Nterms,
   --#                                    Tnt &
   --#         Term_Set              from *,
   --#                                    Nterms,
   --#                                    Tnt,
   --#                                    Token;
   is
   begin
      Result_Define := 0;
      if Tnt then
         if Nnon_Terms >= Sparklalr_Common.Non_Term_Lim then
            Sparklalr_Error.Syn_Error (15, Col);
            SPARK_Ada_Integer_Text_IO.Put_File (File  => F,
                                                Item  => Nnon_Terms,
                                                Width => 6,
                                                Base  => 10);
            SPARK.Ada.Text_IO.Put_File (File => F,
                                        Item => " NONTERMINALS (");
            SPARK_Ada_Integer_Text_IO.Put_File (File  => F,
                                                Item  => Sparklalr_Common.Non_Term_Lim,
                                                Width => 1,
                                                Base  => 10);
            SPARK.Ada.Text_IO.Put_Line_File (File => F,
                                             Item => " MAX)");
         else
            Nnon_Terms             := Nnon_Terms + 1;
            Nterm_Set (Nnon_Terms) := Token;
            Result_Define          := Sparklalr_Common.Nt_Base + Nnon_Terms;
         end if;
      else
         if Nterms >= Sparklalr_Common.Term_Lim then
            Sparklalr_Error.Syn_Error (16, Col);
         else
            Nterms            := Nterms + 1;
            Term_Set (Nterms) := Token;
            if Gram_Rules then
               Sparklalr_Level.Initiate_Term_Lev (Nterms);
            end if;
            Result_Define := Nterms;
         end if;
      end if;
   end Define;

   procedure Print_Sym
     (F    : in out SPARK.Ada.Text_IO.File_Type;
      Sym  : in     Sparklalr_Common.Sym_Range;
      Posn : in out Integer;
      Tab  : in     Integer;
      Comm : in     Boolean)
   --# global in     Nterm_Set;
   --#        in     Term_Set;
   --#        in out SPARK.Ada.Text_IO.The_Standard_Output;
   --# derives F,
   --#         SPARK.Ada.Text_IO.The_Standard_Output from *,
   --#                                                    Comm,
   --#                                                    Nterm_Set,
   --#                                                    Posn,
   --#                                                    Sym,
   --#                                                    Tab,
   --#                                                    Term_Set &
   --#         Posn                                  from *,
   --#                                                    Nterm_Set,
   --#                                                    Sym,
   --#                                                    Tab,
   --#                                                    Term_Set;
   is
      Id : Sparklalr_Common.Id_Name;
      I  : Sparklalr_Common.Id_Length_Count;
   begin
      if Sym > Sparklalr_Common.Nt_Base then
         Id := Nterm_Set (Sym - Sparklalr_Common.Nt_Base);
      else
         Id := Term_Set (Sym);
      end if;
      I := Sparklalr_Common.Id_Length;
      while Id (I) = ' ' loop
         I := I - 1;
      end loop;
      if (Posn + I) > (Sparklalr_Common.Page_Width - 2) then
         SPARK.Ada.Text_IO.New_Line_File (File    => F,
                                          Spacing => 1);
         if Comm then
            SPARK.Ada.Text_IO.Put_File (File => F,
                                        Item => "--");
            Sparklalr_Common.Put_N_Chars (Std_Out => False,
                                          F       => F,
                                          C       => ' ',
                                          N       => Tab - 2);
         else
            Sparklalr_Common.Put_N_Chars (Std_Out => False,
                                          F       => F,
                                          C       => ' ',
                                          N       => Tab);
         end if;
         Posn := I + Tab;
      else
         Posn := Posn + I;
      end if;
      SPARK.Ada.Text_IO.Put_Character_File (File => F,
                                            Item => ' ');
      for J in Integer range 1 .. I loop
         SPARK.Ada.Text_IO.Put_Character_File (File => F,
                                               Item => Id (J));
      end loop;
   end Print_Sym;

   procedure Print_String_Sym
     (F          : in out SPARK.Ada.Text_IO.File_Type;
      String_Var : in     Sparklalr_Common.Id_Name;
      Sym        : in     Sparklalr_Common.Sym_Range;
      Posn       : in out Integer;
      Tab        : in     Integer;
      Comm       : in     Boolean)
   --# global in     Nterm_Set;
   --#        in     Term_Set;
   --#        in out SPARK.Ada.Text_IO.The_Standard_Output;
   --# derives F,
   --#         SPARK.Ada.Text_IO.The_Standard_Output from *,
   --#                                                    Comm,
   --#                                                    Nterm_Set,
   --#                                                    Posn,
   --#                                                    String_Var,
   --#                                                    Sym,
   --#                                                    Tab,
   --#                                                    Term_Set &
   --#         Posn                                  from *,
   --#                                                    Nterm_Set,
   --#                                                    String_Var,
   --#                                                    Sym,
   --#                                                    Tab,
   --#                                                    Term_Set;
   is
      Id   : Sparklalr_Common.Id_Name;
      I, J : Sparklalr_Common.Id_Length_Count;
   begin
      I := Sparklalr_Common.Id_Length;
      while String_Var (I) = ' ' loop
         I := I - 1;
      end loop;
      if Sym > Sparklalr_Common.Nt_Base then
         Id := Nterm_Set (Sym - Sparklalr_Common.Nt_Base);
      else
         Id := Term_Set (Sym);
      end if;
      J := Sparklalr_Common.Id_Length;
      while Id (J) = ' ' loop
         J := J - 1;
      end loop;
      if ((Posn + I) + J) > (Sparklalr_Common.Page_Width - 2) then
         SPARK.Ada.Text_IO.New_Line_File (File    => F,
                                          Spacing => 1);
         if Comm then
            SPARK.Ada.Text_IO.Put_File (File => F,
                                        Item => "--");
            Sparklalr_Common.Put_N_Chars (Std_Out => False,
                                          F       => F,
                                          C       => ' ',
                                          N       => Tab - 2);
         else
            Sparklalr_Common.Put_N_Chars (Std_Out => False,
                                          F       => F,
                                          C       => ' ',
                                          N       => Tab);
         end if;
         Posn := (I + J) + Tab;
      else
         Posn := (Posn + I) + J;
      end if;
      SPARK.Ada.Text_IO.Put_Character_File (File => F,
                                            Item => ' ');
      for K in Integer range 1 .. I loop
         SPARK.Ada.Text_IO.Put_Character_File (File => F,
                                               Item => String_Var (K));
      end loop;
      for K in Integer range 1 .. J loop
         SPARK.Ada.Text_IO.Put_Character_File (File => F,
                                               Item => Id (K));
      end loop;
   end Print_String_Sym;

   procedure Symbol_Strings_Out
   --# global in     Command_Line_Options.State;
   --#        in     Nnon_Terms;
   --#        in     Nterms;
   --#        in     Nterm_Set;
   --#        in     Term_Set;
   --#        in out SPARK.Ada.Command_Line.State;
   --#        in out SPARK.Ada.Text_IO.The_Standard_Error;
   --#        in out SPARK.Ada.Text_IO.The_Standard_Output;
   --# derives SPARK.Ada.Command_Line.State,
   --#         SPARK.Ada.Text_IO.The_Standard_Error  from *,
   --#                                                    Command_Line_Options.State,
   --#                                                    Nnon_Terms,
   --#                                                    Nterms,
   --#                                                    Nterm_Set,
   --#                                                    Term_Set &
   --#         SPARK.Ada.Text_IO.The_Standard_Output from *,
   --#                                                    Nnon_Terms,
   --#                                                    Nterms,
   --#                                                    Nterm_Set,
   --#                                                    Term_Set;
   is
      Sp_Symbols : SPARK.Ada.Text_IO.File_Type;
      Posn       : Integer;
   begin
      SPARK.Ada.Text_IO.Unbounded_String.Create
        (File => Sp_Symbols,
         Mode => SPARK.Ada.Text_IO.Out_File,
         Name => SPARK.Ada.Strings.Unbounded.Concat_Unbounded_String_String
           (Left  => Command_Line_Options.Get_File_Name,
            Right => ".SYM"),
         Form => SPARK.Ada.Strings.Unbounded.Null_Unbounded_String);
      if SPARK.Ada.Text_IO.Get_Last_Exception_File (File => Sp_Symbols) /= SPARK.Ada.Text_IO.No_Exception then
         SPARK.Ada.Text_IO.Put_Error (Item => "Unable to open output SYM file");
         SPARK.Ada.Command_Line.Set_Exit_Status (SPARK.Ada.Command_Line.Failure);
      end if;
      SPARK.Ada.Text_IO.Put_Line_File (File => Sp_Symbols,
                                       Item => "&TERMINALS");
      for Sym in Integer range 0 .. Nterms loop
         Posn := 1;
         --# accept F, 10, Posn, "Ineffective assignment here expected and OK";
         Print_Sym (Sp_Symbols, Sym, Posn, 1, False);
         --# end accept;
         SPARK.Ada.Text_IO.New_Line_File (File    => Sp_Symbols,
                                          Spacing => 1);
      end loop;
      SPARK.Ada.Text_IO.Put_Line_File (File => Sp_Symbols,
                                       Item => "&NONTERMINALS");
      for Sym in Integer range Sparklalr_Common.Nt_Base + 1 .. Sparklalr_Common.Nt_Base + Nnon_Terms loop
         Posn := 1;
         --# accept F, 10, Posn, "Ineffective assignment here expected and OK";
         Print_Sym (Sp_Symbols, Sym, Posn, 5, False);
         --# end accept;
         SPARK.Ada.Text_IO.New_Line_File (File    => Sp_Symbols,
                                          Spacing => 1);
      end loop;
      SPARK.Ada.Text_IO.Put_Line_File (File => Sp_Symbols,
                                       Item => "&END");
      SPARK.Ada.Text_IO.Close (File => Sp_Symbols);
      if SPARK.Ada.Text_IO.Get_Last_Exception_File (File => Sp_Symbols) /= SPARK.Ada.Text_IO.No_Exception then
         SPARK.Ada.Text_IO.Put_Error (Item => "Unable to close output SYM file");
         SPARK.Ada.Command_Line.Set_Exit_Status (SPARK.Ada.Command_Line.Failure);
      end if;
   end Symbol_Strings_Out;

   procedure Find
     (Tnt         : in     Boolean;
      F           : in out SPARK.Ada.Text_IO.File_Type;
      Gram_Rules  : in     Boolean;
      Token       : in     Sparklalr_Common.Id_Name;
      Col         : in     Sparklalr_Error.Err_Col_T;
      Result_Find :    out Integer)
   -- FINDS A (NON) TERMINAL
   --# global in out Nnon_Terms;
   --#        in out Nterms;
   --#        in out Nterm_Set;
   --#        in out Sparklalr_Error.State;
   --#        in out Sparklalr_Level.State;
   --#        in out Term_Set;
   --# derives F                     from *,
   --#                                    Nnon_Terms,
   --#                                    Nterms,
   --#                                    Nterm_Set,
   --#                                    Term_Set,
   --#                                    Tnt,
   --#                                    Token &
   --#         Nnon_Terms,
   --#         Nterms,
   --#         Nterm_Set,
   --#         Result_Find,
   --#         Term_Set              from Nnon_Terms,
   --#                                    Nterms,
   --#                                    Nterm_Set,
   --#                                    Term_Set,
   --#                                    Tnt,
   --#                                    Token &
   --#         Sparklalr_Error.State from *,
   --#                                    Col,
   --#                                    Nnon_Terms,
   --#                                    Nterms,
   --#                                    Nterm_Set,
   --#                                    Term_Set,
   --#                                    Tnt,
   --#                                    Token &
   --#         Sparklalr_Level.State from *,
   --#                                    Gram_Rules,
   --#                                    Nnon_Terms,
   --#                                    Nterms,
   --#                                    Nterm_Set,
   --#                                    Term_Set,
   --#                                    Tnt,
   --#                                    Token;
   is
      I     : Integer;
      Found : Boolean;
   begin
      Result_Find := 0;
      Found       := False;
      I           := 1;
      while (I <= Nterms) and then not Found loop
         if Term_Set (I) = Token then
            Result_Find := I;
            Found       := True;
         else
            I := I + 1;
         end if;
      end loop;
      if not Found then
         I     := 1;
         Found := False;
         while (I <= Nnon_Terms) and then not Found loop
            if Nterm_Set (I) = Token then
               Found       := True;
               Result_Find := I + Sparklalr_Common.Nt_Base;
            else
               I := I + 1;
            end if;
         end loop;
         if not Found then
            Define (Tnt, F, Gram_Rules, Token, Col, Result_Find);
         end if;
      end if;
   end Find;

   -- -------- The following procedures print out the SPARK parser tables ----
   procedure Symbols_Package_Out (F : in out SPARK.Ada.Text_IO.File_Type)
   --# global in     Nnon_Terms;
   --#        in     Nterms;
   --#        in     Nterm_Set;
   --#        in     Term_Set;
   --#        in out SPARK.Ada.Text_IO.The_Standard_Output;
   --# derives F,
   --#         SPARK.Ada.Text_IO.The_Standard_Output from *,
   --#                                                    Nnon_Terms,
   --#                                                    Nterms,
   --#                                                    Nterm_Set,
   --#                                                    Term_Set;
   is
      Posn : Integer;
   begin
      SPARK.Ada.Text_IO.Put_Line_File (File => F,
                                       Item => "package SP_Symbols is");
      SPARK.Ada.Text_IO.New_Line_File (File    => F,
                                       Spacing => 1);
      SPARK.Ada.Text_IO.Put_Line_File (File => F,
                                       Item => "   type SP_Symbol is (");
      Posn := 21;
      Sparklalr_Common.Put_N_Chars (Std_Out => False,
                                    F       => F,
                                    C       => ' ',
                                    N       => 21);
      for I in Integer range 0 .. Nterms loop
         Print_Sym (F, I, Posn, 21, False);
         SPARK.Ada.Text_IO.Put_Character_File (File => F,
                                               Item => ',');
         Posn := Posn + 2;
      end loop;
      SPARK.Ada.Text_IO.New_Line_File (File    => F,
                                       Spacing => 1);
      SPARK.Ada.Text_IO.New_Line_File (File    => F,
                                       Spacing => 1);
      Sparklalr_Common.Put_N_Chars (Std_Out => False,
                                    F       => F,
                                    C       => ' ',
                                    N       => 21);
      Posn := 21;
      for I in Integer range Sparklalr_Common.Nt_Base + 1 .. (Sparklalr_Common.Nt_Base + Nnon_Terms) - 1 loop
         Print_Sym (F, I, Posn, 21, False);
         SPARK.Ada.Text_IO.Put_Character_File (File => F,
                                               Item => ',');
         Posn := Posn + 2;
      end loop;
      --# accept F, 10, Posn, "Ineffective assignment here expected and OK";
      Print_Sym (F, Sparklalr_Common.Nt_Base + Nnon_Terms, Posn, 21, False);
      --# end accept;
      SPARK.Ada.Text_IO.New_Line_File (File    => F,
                                       Spacing => 1);
      SPARK.Ada.Text_IO.Put_Line_File (File => F,
                                       Item => "                   );");
      SPARK.Ada.Text_IO.Put_File (File => F,
                                  Item => "   subtype SP_Terminal             is SP_Symbol range");
      Posn := 40;
      Print_Sym (F, 0, Posn, 10, False);
      SPARK.Ada.Text_IO.Put_File (File => F,
                                  Item => " ..");
      Posn := Posn + 2;
      --# accept F, 10, Posn, "Ineffective assignment here expected and OK";
      Print_Sym (F, Nterms, Posn, 10, False);
      --# end accept;
      SPARK.Ada.Text_IO.Put_Line_File (File => F,
                                       Item => ";");
      SPARK.Ada.Text_IO.Put_File (File => F,
                                  Item => "   subtype SP_Non_Terminal         is SP_Symbol range");
      Posn := 42;
      Print_Sym (F, Sparklalr_Common.Nt_Base + 1, Posn, 10, False);
      SPARK.Ada.Text_IO.Put_File (File => F,
                                  Item => " ..");
      Posn := Posn + 2;
      --# accept F, 10, Posn, "Ineffective assignment here expected and OK";
      Print_Sym (F, Sparklalr_Common.Nt_Base + Nnon_Terms, Posn, 10, False);
      --# end accept;
      SPARK.Ada.Text_IO.Put_Line_File (File => F,
                                       Item => ";");
      SPARK.Ada.Text_IO.Put_File (File => F,
                                  Item => "   subtype SP_Grammar_Non_Terminal is SP_Symbol range");
      Posn := 42;
      Print_Sym (F, Sparklalr_Common.Nt_Base + 2, Posn, 10, False);
      SPARK.Ada.Text_IO.Put_File (File => F,
                                  Item => " ..");
      Posn := Posn + 2;
      --# accept F, 10, Posn, "Ineffective assignment here expected and OK";
      Print_Sym (F, Sparklalr_Common.Nt_Base + Nnon_Terms, Posn, 10, False);
      --# end accept;
      SPARK.Ada.Text_IO.Put_Line_File (File => F,
                                       Item => ";");
      SPARK.Ada.Text_IO.New_Line_File (File    => F,
                                       Spacing => 1);
      SPARK.Ada.Text_IO.Put_Line_File (File => F,
                                       Item => "end SP_Symbols;");
   end Symbols_Package_Out;

   function Get_Nterms return Integer
   --# global in Nterms;
   is
   begin
      return Nterms;
   end Get_Nterms;

   function Get_Nnon_Terms return Integer
   --# global in Nnon_Terms;
   is
   begin
      return Nnon_Terms;
   end Get_Nnon_Terms;

   function Get_Term_Set (I : in Sparklalr_Common.Term_Range) return Sparklalr_Common.Id_Name
   --# global in Term_Set;
   is
   begin
      return Term_Set (I);
   end Get_Term_Set;

   function Get_Nterm_Set (I : in Sparklalr_Common.Non_Term_Range) return Sparklalr_Common.Id_Name
   --# global in Nterm_Set;
   is
   begin
      return Nterm_Set (I);
   end Get_Nterm_Set;

end Symbols_Dump;
