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

--------------------------------------------------------------------------------
--  ComponentManager
--
--  Purpose:
--   The ComponentManager package serves the purpose of keeping track of
--  the components (fields) of record types and variables. It offers
--  an ADT (ComponentData) and a set of operations on this to support an
--  arbitrary number of records.
--
--   The records are organised in a tree structure, with a number of roots
--  being the top-level records and branches going down through subrecords
--  (or tagged-type extensions) to the leaves which are scalar variables.
--
--   At a lower level view, we have a large number of records within an array
--  structure (array of ComponentDescriptor), each record containing many
--  pointers to logically adjacent records using indexes of type Component.
--  Pointers are maintained to FirstChild, and then each Child has a pointer
--  to its next sibling, enabling access to all children. Each node also
--  maintains a list of errors stored in a SeqAlgebra.
--
--   The debug procedures at the bottom of this package offer an easy way
--  of exploring the layout of a particular instance of the ADT. Debugging
--  output using them is enabled with the -debug=c switch.
--
--  Clients:
--    Sem.Compunit and subunits
--    FlowAnalyser
--
--  Use:
--
--  Extension:
--     None planned.
--------------------------------------------------------------------------------

with ComponentErrors;
with Dictionary;
with ExaminerConstants;
with Heap;
with SeqAlgebra;

use type Dictionary.Symbol;

--# inherit CommandLineData,
--#         ComponentErrors,
--#         Debug,
--#         Dictionary,
--#         ExaminerConstants,
--#         Heap,
--#         SeqAlgebra,
--#         Statistics,
--#         SystemErrors;

package ComponentManager is

   type Component is private;
   type ComponentData is private;

   procedure Initialise (Data : out ComponentData);
   --# derives Data from ;

   procedure AddRoot (Data    : in out ComponentData;
                      HeapSeq : in out Heap.HeapRecord;
                      RootSym : in     Dictionary.Symbol);
   --# global in out Statistics.TableUsage;
   --# derives Data,
   --#         HeapSeq,
   --#         Statistics.TableUsage from *,
   --#                                    Data,
   --#                                    HeapSeq,
   --#                                    RootSym;
   --  pre Dictionary.IsVariable(RootSym) and
   --      Dictionary.TypeIsRecord(Dictionary.GetType(RootSym));
   -- Adds a record variable as a root node.  The name of the
   -- node will be RootSym.  The Hash link will be used to
   -- hash the node with others whose names have the same hash
   -- value. The node is created with null Parent, FirstChild,
   -- NextSibling, PreviousSibling and an empty ListOfErrors.

   procedure AddNextChild
     (Data     : in out ComponentData;
      HeapSeq  : in out Heap.HeapRecord;
      Node     : in     Component;
      ChildSym : in     Dictionary.Symbol);
   --# global in     CommandLineData.Content;
   --#        in out Statistics.TableUsage;
   --# derives Data,
   --#         HeapSeq,
   --#         Statistics.TableUsage from *,
   --#                                    ChildSym,
   --#                                    Data,
   --#                                    HeapSeq,
   --#                                    Node &
   --#         null                  from CommandLineData.Content;
   --  pre Dictionary.IsRecordVarComponent(ChildSym);
   -- Creates ChildNode and adds it as the next child of Node.
   -- The name of ChildNode will be ChildSym.  The Hash link
   -- will be used to hash the node with others whose names
   -- have the same hash value. The parent of ChildNode is
   -- Node and is created with null FirstChild,
   -- NextSibling, PreviousSibling and an empty ListOfErrors.
   -- Duplicate insertions are ignored (CFR 1766)

   function GetComponentNode (Data : ComponentData;
                              Sym  : Dictionary.Symbol) return Component;
   --  pre Dictionary.IsVariable(Sym) and
   --      Dictionary.TypeIsRecord(Dictionary.GetType(Sym))
   --      or Dictionary.IsRecordVarComponent(Sym);
   -- Returns the node with Sym as name.

   function HasChildren (Data : ComponentData;
                         Node : Component) return Boolean;
   -- Returns true if Node has any children; otherwise false.

   function IsNullComponent (Node : Component) return Boolean;
   -- Returns true if Node is null; otherwise false.

   function IsALeaf (Data : ComponentData;
                     Node : Component) return Boolean;
   -- Returns true if Node is a leaf; otherwise false.

   --647--
   function IsARoot (Data : ComponentData;
                     Node : Component) return Boolean;
   -- Returns true if Node is a root; otherwise false.

   function IsTransitiveParent
     (Data   : ComponentData;
      Parent : Component;
      Node   : Component)
     return   Boolean;
   -- Returns true if Parent can be obtained from Node by a
   -- series of zero or more GetParent calls.  Note that the
   -- function considers a node to be a transitive parent of
   -- itself.

   function GetRoot (Data : ComponentData;
                     Node : Component) return Component;
   -- Returns the root of the tree of which Node is part.
   -- If Node itself is a root it returns Node.

   function GetParent (Data : ComponentData;
                       Node : Component) return Component;
   -- Returns the immediate parent of Node.  Returns null
   -- if Node is a root.

   function GetFirstChild (Data : ComponentData;
                           Node : Component) return Component;
   -- Returns the first child of Node.  Returns null if Node
   -- has no children.
   --

   function GetNextSibling (Data : ComponentData;
                            Node : Component) return Component;
   -- Returns the next sibling of Node.  Result is null if
   -- Node has no next sibling i.e. it is the last child of
   -- its parent.

   function GetPreviousSibling (Data : ComponentData;
                                Node : Component) return Component;
   -- Returns the previous sibling of Node.  Result is null if
   -- Node has no previous sibling i.e. it is the first child
   -- of its parent.

   function GetName (Data : ComponentData;
                     Node : Component) return Dictionary.Symbol;
   -- Returns the name of Node.

   procedure GetLeaves
     (HeapSeq        : in out Heap.HeapRecord;
      Data           : in     ComponentData;
      Node           : in     Component;
      SeqOfLeafNames :    out SeqAlgebra.Seq);
   --# global in out Statistics.TableUsage;
   --# derives HeapSeq,
   --#         Statistics.TableUsage from *,
   --#                                    Data,
   --#                                    HeapSeq,
   --#                                    Node &
   --#         SeqOfLeafNames        from HeapSeq;
   -- This returns a sequence of the names of all leaf nodes
   -- which are below Node.  The sequence is created on
   -- HeapSeq (and not on the ComponentData heap).
   -- If Node is a leaf an empty sequence is returned.

   procedure AddError
     (HeapSeq      : in out Heap.HeapRecord;
      TheErrorHeap : in     ComponentErrors.HeapOfErrors;
      Data         : in     ComponentData;
      Node         : in     Component;
      NewError     : in     Natural);
   --# global in out Statistics.TableUsage;
   --# derives HeapSeq,
   --#         Statistics.TableUsage from *,
   --#                                    Data,
   --#                                    HeapSeq,
   --#                                    NewError,
   --#                                    Node,
   --#                                    TheErrorHeap;
   -- Adds NewError in the list of errors for Node.

   function GetListOfErrors (Data : ComponentData;
                             Node : Component) return SeqAlgebra.Seq;
   -- Returns the sequence of errors on Node.

   procedure AddNewListOfErrors
     (HeapSeq      : in out Heap.HeapRecord;
      Data         : in out ComponentData;
      Node         : in     Component;
      NewErrorList : in     SeqAlgebra.Seq);
   --# derives Data    from *,
   --#                      NewErrorList,
   --#                      Node &
   --#         HeapSeq from *,
   --#                      Data,
   --#                      Node;
   -- Dispose of existing ListOfErrors (including the case where this
   -- is an empty sequence) on Node and set it to NewErrorList.

   procedure EmptyListOfErrors (HeapSeq : in out Heap.HeapRecord;
                                Data    : in out ComponentData;
                                Node    : in     Component);
   --# global in out Statistics.TableUsage;
   --# derives Data                  from *,
   --#                                    HeapSeq,
   --#                                    Node &
   --#         HeapSeq,
   --#         Statistics.TableUsage from *,
   --#                                    HeapSeq;
   -- Set the ListOfErrors on Node to a new empty sequence.  Do not
   -- dispose of the existing list of errors on Node as this may have
   -- been added to another node by AddNewListOfErrors.

   -- New function for use by MergeAndHandleErrors
   function GetFirstRoot (Data : ComponentData) return Component;

   -- New function for use by MergeAndHandleErrors
   function GetNextRoot (Data     : ComponentData;
                         RootNode : Component) return Component;

   function ComponentToRef (C : Component) return Natural;

   function RefToComponent (N : Natural) return Component;

   procedure ReportUsage (Data : in ComponentData);
   --# global in out Statistics.TableUsage;
   --# derives Statistics.TableUsage from *,
   --#                                    Data;

   -- Outputs a tree of the components starting at Node
   procedure Dump_Component_Tree (Data        : in ComponentData;
                                  Node        : in Component;
                                  Indentation : in Natural);
   --# derives null from Data,
   --#                   Indentation,
   --#                   Node;

   -- Outputs trees of all components in the manager
   procedure Dump_All_Component_Trees (Data : in ComponentData);
   --# derives null from Data;

private

   -- Component Tree Structure
   MaxNumComponents : constant := ExaminerConstants.MaxRecordComponents;

   type Component is range 0 .. MaxNumComponents;
   subtype ComponentIndex is Component range 1 .. MaxNumComponents;

   NullComponent : constant Component := 0;

   type ComponentDescriptor is record
      Name                                                                        : Dictionary.Symbol;
      ListOfErrors                                                                : SeqAlgebra.Seq;
      NextRoot, Hash, Parent, FirstChild, LastChild, --not in AAk's design, added for efficiency reasons
        NextSibling, PreviousSibling                                              : Component;
   end record;

   type ArrayOfComponents is array (ComponentIndex) of ComponentDescriptor;

   type HeapOfComponents is record
      ListOfComponents : ArrayOfComponents;
      FirstRoot        : Component;
      HighMark         : Component;
   end record;

   --Hash Table of Root Components

   HashMax : constant Integer := MaxNumComponents / 10;
   subtype HashIndex is Integer range 0 .. HashMax;

   type HashTable is array (HashIndex) of Component;

   type ComponentData is record
      TheHeap  : HeapOfComponents;
      TheTable : HashTable;
   end record;

end ComponentManager;
