-------------------------------------------------------------------------------
-- (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.
--
--=============================================================================

separate (Sem)
package body Aggregate_Stack
--# own State is S,
--#              Top_Ptr;
is
   type Typ_Entry is record
      Type_Sym     : Dictionary.Symbol;
      Lower_Bound  : Sem.Typ_Type_Bound;
      Upper_Bound  : Sem.Typ_Type_Bound;
      Agg_Flags    : Sem.Typ_Agg_Flags;
      Counter      : Natural;
      Complete_Rec : CompleteCheck.T;
   end record;

   Null_Entry : constant Typ_Entry :=
     Typ_Entry'
     (Type_Sym     => Dictionary.NullSymbol,
      Lower_Bound  => Sem.Unknown_Type_Bound,
      Upper_Bound  => Sem.Unknown_Type_Bound,
      Agg_Flags    => Sem.Null_Typ_Agg_Flags,
      Counter      => Natural'First,
      Complete_Rec => CompleteCheck.NullT);

   subtype Index_Range is Integer range 1 .. ExaminerConstants.AggregateStackMax;
   type Stack_Array is array (Index_Range) of Typ_Entry;
   subtype Top_Range is Integer range 0 .. ExaminerConstants.AggregateStackMax;

   S       : Stack_Array;
   Top_Ptr : Top_Range;

   procedure Init
   --# global in     Dictionary.Dict;
   --#           out S;
   --#           out Top_Ptr;
   --# derives S,
   --#         Top_Ptr from  &
   --#         null    from Dictionary.Dict;
   --# post (for all I in Index_Range range Index_Range'First .. Top_Ptr =>
   --#         ((Dictionary.Is_Null_Symbol (S(I).Type_Sym) or Dictionary.IsTypeMark (S(I).Type_Sym, Dictionary.Dict)) and
   --#            ((S(I).Lower_Bound.Is_Defined and S(I).Upper_Bound.Is_Defined) ->
   --#               (S(I).Lower_Bound.Value <= S(I).Upper_Bound.Value)) and
   --#            (S(I).Complete_Rec.ActualUpperBound - S(I).Complete_Rec.LowerBound < ExaminerConstants.CompleteCheckSize)));
   is
   begin
      Top_Ptr := 0;
      S       := Stack_Array'(others => Null_Entry);
      --# accept Flow, 30, Dictionary.Dict, "Variable not referenced nor exported OK";
   end Init;

   procedure Push
     (Type_Sym     : in Dictionary.Symbol;
      Lower_Bound  : in Sem.Typ_Type_Bound;
      Upper_Bound  : in Sem.Typ_Type_Bound;
      Agg_Flags    : in Sem.Typ_Agg_Flags;
      Counter      : in Natural;
      Complete_Rec : in CompleteCheck.T)
   --# global in     Dictionary.Dict;
   --#        in out S;
   --#        in out Top_Ptr;
   --# derives S       from *,
   --#                      Agg_Flags,
   --#                      Complete_Rec,
   --#                      Counter,
   --#                      Lower_Bound,
   --#                      Top_Ptr,
   --#                      Type_Sym,
   --#                      Upper_Bound &
   --#         Top_Ptr from * &
   --#         null    from Dictionary.Dict;
   --# pre (for all I in Index_Range range Index_Range'First .. Top_Ptr =>
   --#        ((Dictionary.Is_Null_Symbol (S(I).Type_Sym) or Dictionary.IsTypeMark (S(I).Type_Sym, Dictionary.Dict)) and
   --#           ((S(I).Lower_Bound.Is_Defined and S(I).Upper_Bound.Is_Defined) ->
   --#              (S(I).Lower_Bound.Value <= S(I).Upper_Bound.Value)) and
   --#           (S(I).Complete_Rec.ActualUpperBound - S(I).Complete_Rec.LowerBound < ExaminerConstants.CompleteCheckSize))) and
   --#   (Dictionary.Is_Null_Symbol (Type_Sym) or Dictionary.IsTypeMark (Type_Sym, Dictionary.Dict)) and
   --#   ((Lower_Bound.Is_Defined and Upper_Bound.Is_Defined) -> (Lower_Bound.Value <= Upper_Bound.Value)) and
   --#   (Complete_Rec.ActualUpperBound - Complete_Rec.LowerBound < ExaminerConstants.CompleteCheckSize);
   --# post (for all I in Index_Range range Index_Range'First .. Top_Ptr =>
   --#         ((Dictionary.Is_Null_Symbol (S(I).Type_Sym) or Dictionary.IsTypeMark (S(I).Type_Sym, Dictionary.Dict)) and
   --#            ((S(I).Lower_Bound.Is_Defined and S(I).Upper_Bound.Is_Defined) ->
   --#               (S(I).Lower_Bound.Value <= S(I).Upper_Bound.Value)) and
   --#            (S(I).Complete_Rec.ActualUpperBound - S(I).Complete_Rec.LowerBound < ExaminerConstants.CompleteCheckSize)));
   is
   begin
      if Top_Ptr = ExaminerConstants.AggregateStackMax then
         SystemErrors.Fatal_Error (Sys_Err => SystemErrors.Aggregate_Stack_Over_Flow,
                                   Msg     => "in Aggregate_Stack.Push");
      end if;

      --# check Top_Ptr < ExaminerConstants.AggregateStackMax;

      Top_Ptr     := Top_Ptr + 1;
      S (Top_Ptr) :=
        Typ_Entry'
        (Type_Sym     => Type_Sym,
         Lower_Bound  => Lower_Bound,
         Upper_Bound  => Upper_Bound,
         Agg_Flags    => Agg_Flags,
         Counter      => Counter,
         Complete_Rec => Complete_Rec);
      --# accept Flow, 30, Dictionary.Dict, "Variable not referenced nor exported OK";
   end Push;

   procedure Pop
     (Type_Sym     : out Dictionary.Symbol;
      Lower_Bound  : out Sem.Typ_Type_Bound;
      Upper_Bound  : out Sem.Typ_Type_Bound;
      Agg_Flags    : out Sem.Typ_Agg_Flags;
      Counter      : out Natural;
      Complete_Rec : out CompleteCheck.T)
   --# global in     Dictionary.Dict;
   --#        in     S;
   --#        in out Top_Ptr;
   --# derives Agg_Flags,
   --#         Complete_Rec,
   --#         Counter,
   --#         Lower_Bound,
   --#         Type_Sym,
   --#         Upper_Bound  from S,
   --#                           Top_Ptr &
   --#         Top_Ptr      from * &
   --#         null         from Dictionary.Dict;
   --# pre (for all I in Index_Range range Index_Range'First .. Top_Ptr =>
   --#        ((Dictionary.Is_Null_Symbol (S(I).Type_Sym) or Dictionary.IsTypeMark (S(I).Type_Sym, Dictionary.Dict)) and
   --#           ((S(I).Lower_Bound.Is_Defined and S(I).Upper_Bound.Is_Defined) ->
   --#              (S(I).Lower_Bound.Value <= S(I).Upper_Bound.Value)) and
   --#           (S(I).Complete_Rec.ActualUpperBound - S(I).Complete_Rec.LowerBound < ExaminerConstants.CompleteCheckSize)));
   --# post (for all I in Index_Range range Index_Range'First .. Top_Ptr =>
   --#         ((Dictionary.Is_Null_Symbol (S(I).Type_Sym) or Dictionary.IsTypeMark (S(I).Type_Sym, Dictionary.Dict)) and
   --#            ((S(I).Lower_Bound.Is_Defined and S(I).Upper_Bound.Is_Defined) ->
   --#               (S(I).Lower_Bound.Value <= S(I).Upper_Bound.Value)) and
   --#            (S(I).Complete_Rec.ActualUpperBound - S(I).Complete_Rec.LowerBound < ExaminerConstants.CompleteCheckSize))) and
   --#   (Dictionary.Is_Null_Symbol (Type_Sym) or Dictionary.IsTypeMark (Type_Sym, Dictionary.Dict)) and
   --#   ((Lower_Bound.Is_Defined and Upper_Bound.Is_Defined) -> (Lower_Bound.Value <= Upper_Bound.Value)) and
   --#   (Complete_Rec.ActualUpperBound - Complete_Rec.LowerBound < ExaminerConstants.CompleteCheckSize);
   is
   begin
      if Top_Ptr = 0 then
         SystemErrors.Fatal_Error (Sys_Err => SystemErrors.Aggregate_Stack_Under_Flow,
                                   Msg     => "in Aggregate_Stack.Pop");
      end if;

      --# check Top_Ptr > 0;

      Type_Sym     := S (Top_Ptr).Type_Sym;
      Lower_Bound  := S (Top_Ptr).Lower_Bound;
      Upper_Bound  := S (Top_Ptr).Upper_Bound;
      Agg_Flags    := S (Top_Ptr).Agg_Flags;
      Counter      := S (Top_Ptr).Counter;
      Complete_Rec := S (Top_Ptr).Complete_Rec;
      Top_Ptr      := Top_Ptr - 1;
      --# accept Flow, 30, Dictionary.Dict, "Variable not referenced nor exported OK";
   end Pop;

   function Top_Type_Sym return  Dictionary.Symbol
   --# global in Dictionary.Dict;
   --#        in S;
   --#        in Top_Ptr;
   --# pre (for all I in Index_Range range Index_Range'First .. Top_Ptr =>
   --#        ((Dictionary.Is_Null_Symbol (S(I).Type_Sym) or Dictionary.IsTypeMark (S(I).Type_Sym, Dictionary.Dict)) and
   --#           ((S(I).Lower_Bound.Is_Defined and S(I).Upper_Bound.Is_Defined) ->
   --#              (S(I).Lower_Bound.Value <= S(I).Upper_Bound.Value)) and
   --#           (S(I).Complete_Rec.ActualUpperBound - S(I).Complete_Rec.LowerBound < ExaminerConstants.CompleteCheckSize)));
   --# return Type_Sym => (Dictionary.Is_Null_Symbol (Type_Sym) or Dictionary.IsTypeMark (Type_Sym, Dictionary.Dict));
   is
      Result : Dictionary.Symbol := Dictionary.NullSymbol;
   begin
      if Top_Ptr > 0 then
         Result := S (Top_Ptr).Type_Sym;
      end if;
      --# accept Flow, 30, Dictionary.Dict, "Variable not referenced nor exported OK" &
      --#        Flow, 50, Dictionary.Dict, "Value is not derived from the imported value OK";
      return Result;
   end Top_Type_Sym;

end Aggregate_Stack;
