-------------------------------------------------------------------------------
-- (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 Ada.Text_IO;

separate (Sem.Walk_Expression_P)
package body Exp_Stack is

   procedure Debug_Item (Op   : in String;
                         Item : in Sem.Exp_Record)
   --# derives null from Item,
   --#                   Op;
   is
      --# hide Debug_Item;
   begin
      Ada.Text_IO.Put_Line (Op);
      if not Dictionary.Is_Null_Symbol (Item.Type_Symbol) then
         Ada.Text_IO.Put ("   Sem.Exp_Record.TypeSymbol = ");
         E_Strings.Put_Line
           (File  => SPARK_IO.Standard_Output,
            E_Str => Dictionary.GenerateSimpleName (Item      => Item.Type_Symbol,
                                                    Separator => "."));
      end if;
      if not Dictionary.Is_Null_Symbol (Item.Other_Symbol) then
         Ada.Text_IO.Put ("   Sem.Exp_Record.OtherSymbol = ");
         E_Strings.Put_Line
           (File  => SPARK_IO.Standard_Output,
            E_Str => Dictionary.GenerateSimpleName (Item      => Item.Other_Symbol,
                                                    Separator => "."));
      end if;
      if not Dictionary.Is_Null_Symbol (Item.Stream_Symbol) then
         Ada.Text_IO.Put ("   Sem.Exp_Record.StreamSymbol = ");
         E_Strings.Put_Line
           (File  => SPARK_IO.Standard_Output,
            E_Str => Dictionary.GenerateSimpleName (Item      => Item.Stream_Symbol,
                                                    Separator => "."));
      end if;
      if not Dictionary.Is_Null_Symbol (Item.Tagged_Parameter_Symbol) then
         Ada.Text_IO.Put ("   Sem.Exp_Record.TaggedParameterSymbol = ");
         E_Strings.Put_Line
           (File  => SPARK_IO.Standard_Output,
            E_Str => Dictionary.GenerateSimpleName (Item      => Item.Tagged_Parameter_Symbol,
                                                    Separator => "."));
      end if;
      if not Dictionary.Is_Null_Symbol (Item.Variable_Symbol) then
         Ada.Text_IO.Put ("   Sem.Exp_Record.VariableSymbol = ");
         E_Strings.Put_Line
           (File  => SPARK_IO.Standard_Output,
            E_Str => Dictionary.GenerateSimpleName (Item      => Item.Variable_Symbol,
                                                    Separator => "."));
      end if;
   end Debug_Item;

   procedure Debug_Stack (Stack : in Exp_Stack_Type)
   --# derives null from Stack;
   is
      --# hide Debug_Stack;
   begin
      Ada.Text_IO.Put_Line ("----------------------------------------");
      for I in Index_Range'First .. Stack.Top_Ptr loop
         Debug_Item (Op   => "Exp_Stack.Debug_Stack",
                     Item => Stack.S (I));
      end loop;
      Ada.Text_IO.Put_Line ("----------------------------------------");
   end Debug_Stack;

   procedure Init (Stack : out Exp_Stack_Type) is
   begin
      Stack.Top_Ptr := 0;
      --# accept Flow, 32, Stack.S, "Safe partial initialisation" &
      --#        Flow, 31, Stack.S, "Safe partial initialisation" &
      --#        Flow, 602, Stack, Stack.S, "Safe partial initialisation";
   end Init;

   procedure Push (X     : in     Sem.Exp_Record;
                   Stack : in out Exp_Stack_Type) is
   begin
      SystemErrors.RT_Assert
        (C       => (Dictionary.Is_Null_Symbol (X.Type_Symbol) or else Dictionary.IsTypeMark (X.Type_Symbol))
           and then (Dictionary.Is_Null_Symbol (X.Stream_Symbol)
                       or else Dictionary.IsFunction (X.Stream_Symbol)
                       or else Dictionary.IsOwnVariableOrConstituentWithMode (X.Stream_Symbol)),
         Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Bad X in Exp_Stack.Push");
      if CommandLineData.Content.Debug.Expressions then
         Debug_Item ("Exp_Stack.Push", X);
      end if;

      if Stack.Top_Ptr < ExaminerConstants.WalkExpStackMax then
         Stack.Top_Ptr           := Stack.Top_Ptr + 1;
         Stack.S (Stack.Top_Ptr) := X;
      else
         SystemErrors.Fatal_Error (Sys_Err => SystemErrors.Expression_Stack_Overflow,
                                   Msg     => "in Exp_Stack.Push");
      end if;

      if CommandLineData.Content.Debug.Expressions then
         Debug_Stack (Stack);
      end if;
   end Push;

   procedure Pop (Item  :    out Sem.Exp_Record;
                  Stack : in out Exp_Stack_Type) is
   begin
      if Stack.Top_Ptr > 0 then
         Item          := Stack.S (Stack.Top_Ptr);
         Stack.Top_Ptr := Stack.Top_Ptr - 1;

         if CommandLineData.Content.Debug.Expressions then
            Debug_Item ("Exp_Stack.Pop", Item);
            Debug_Stack (Stack);
         end if;

      else
         SystemErrors.Fatal_Error (Sys_Err => SystemErrors.Expression_Stack_Underflow,
                                   Msg     => "in Exp_Stack.Pop");
         Item := Stack.S (Stack.Top_Ptr);
      end if;
      SystemErrors.RT_Assert
        (C       => (Dictionary.Is_Null_Symbol (Item.Type_Symbol) or else Dictionary.IsTypeMark (Item.Type_Symbol))
           and then (Dictionary.Is_Null_Symbol (Item.Stream_Symbol)
                       or else Dictionary.IsFunction (Item.Stream_Symbol)
                       or else Dictionary.IsOwnVariableOrConstituentWithMode (Item.Stream_Symbol)),
         Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Bad Item in Exp_Stack.Pop");
   end Pop;

   function Top (Stack : Exp_Stack_Type) return Sem.Exp_Record is
      Result : Sem.Exp_Record;
   begin
      if Stack.Top_Ptr > 0 then
         Result := Stack.S (Stack.Top_Ptr);
      else
         Result := Sem.Null_Exp_Record;
      end if;
      SystemErrors.RT_Assert
        (C       => (Dictionary.Is_Null_Symbol (Result.Type_Symbol) or else Dictionary.IsTypeMark (Result.Type_Symbol))
           and then (Dictionary.Is_Null_Symbol (Result.Stream_Symbol)
                       or else Dictionary.IsFunction (Result.Stream_Symbol)
                       or else Dictionary.IsOwnVariableOrConstituentWithMode (Result.Stream_Symbol)),
         Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Bad Result in Exp_Stack.Top");
      --# accept Flow, 35, Dictionary.Dict, "Importation of the initial value is ineffective OK" &
      --#        Flow, 50, Dictionary.Dict, "Value is not derived from the imported value OK";
      return Result;
   end Top;

   function Has_One_Entry (Stack : Exp_Stack_Type) return Boolean is
   begin
      return Stack.Top_Ptr = 1;
   end Has_One_Entry;

   function Is_Empty (Stack : Exp_Stack_Type) return Boolean is
   begin
      return Stack.Top_Ptr = 0;
   end Is_Empty;

end Exp_Stack;
