-------------------------------------------------------------------------------
-- (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.CompUnit.WalkStatements)
procedure Down_Loop (Node  : in     STree.SyntaxNode;
                     Scope : in out Dictionary.Scopes) is
   Ident_Node            : STree.SyntaxNode;
   Iteration_Scheme_Node : STree.SyntaxNode;
   Loop_Sym              : Dictionary.Symbol;
   Loop_Ident            : LexTokenManager.Lex_String;
begin
   Dictionary.AddLoop
     (Scope         => Scope,
      Comp_Unit     => ContextManager.Ops.Current_Unit,
      LoopStatement => Dictionary.Location'(Start_Position => Node_Position (Node => Node),
                                            End_Position   => Node_Position (Node => Node)),
      TheLoop       => Loop_Sym);

   Iteration_Scheme_Node := Child_Node (Current_Node => Node);
   -- ASSUME Iteration_Scheme_Node = simple_name OR loop_statement_opt
   if Syntax_Node_Type (Node => Iteration_Scheme_Node) = SP_Symbols.simple_name then
      -- ASSUME Iteration_Scheme_Node = simple_name
      Ident_Node := Child_Node (Current_Node => Iteration_Scheme_Node);
      -- ASSUME Ident_Node = identifier
      SystemErrors.RT_Assert
        (C       => Syntax_Node_Type (Node => Ident_Node) = SP_Symbols.identifier,
         Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Expect Ident_Node = identifier in Down_Loop");
      Loop_Ident := Node_Lex_String (Node => Ident_Node);
      if Dictionary.IsDefined
        (Name              => Loop_Ident,
         Scope             => Scope,
         Context           => Dictionary.ProofContext,
         Full_Package_Name => False) then
         ErrorHandler.Semantic_Error
           (Err_Num   => 10,
            Reference => ErrorHandler.No_Reference,
            Position  => Node_Position (Node => Ident_Node),
            Id_Str    => Loop_Ident);
      else
         Dictionary.AddLoopName (Loop_Ident, Loop_Sym);
      end if;
      Iteration_Scheme_Node := Next_Sibling (Current_Node => Iteration_Scheme_Node); -- skip loop name
   elsif Syntax_Node_Type (Node => Iteration_Scheme_Node) /= SP_Symbols.loop_statement_opt then
      SystemErrors.Fatal_Error
        (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Expect Iteration_Scheme_Node = simple_name OR loop_statement_opt in Down_Loop");
   end if;

   -- ASSUME Iteration_Scheme_Node = loop_statement_opt
   SystemErrors.RT_Assert
     (C       => Syntax_Node_Type (Node => Iteration_Scheme_Node) = SP_Symbols.loop_statement_opt,
      Sys_Err => SystemErrors.Invalid_Syntax_Tree,
      Msg     => "Expect Iteration_Scheme_Node = loop_statement_opt in Down_Loop");

   -- check for an iteration scheme, if one is found then mark loop as having exits
   Iteration_Scheme_Node := Child_Node (Current_Node => Iteration_Scheme_Node);
   -- ASSUME Iteration_Scheme_Node = iteration_scheme OR NULL
   if Syntax_Node_Type (Node => Iteration_Scheme_Node) = SP_Symbols.iteration_scheme then
      -- ASSUME Iteration_Scheme_Node = iteration_scheme
      Dictionary.MarkLoopHasExits (Loop_Sym);
   elsif Iteration_Scheme_Node /= STree.NullNode then
      SystemErrors.Fatal_Error
        (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Expect Iteration_Scheme_Node = iteration_scheme OR NULL in Down_Loop");
   end if;
   Scope := Dictionary.Set_Visibility (The_Visibility => Dictionary.Local,
                                       The_Unit       => Loop_Sym);
end Down_Loop;
