-------------------------------------------------------------------------------
-- (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)
procedure Up_Wf_Task_Body (Node  : in     STree.SyntaxNode;
                           Scope : in out Dictionary.Scopes) is
   Task_Sym : Dictionary.Symbol;
   End_Node : STree.SyntaxNode;

   procedure Check_For_Endless_Loop (Node     : in STree.SyntaxNode;
                                     Task_Sym : in Dictionary.Symbol)
   --# global in     CommandLineData.Content;
   --#        in     Dictionary.Dict;
   --#        in     LexTokenManager.State;
   --#        in     STree.Table;
   --#        in out ErrorHandler.Error_Context;
   --#        in out SPARK_IO.File_Sys;
   --# derives ErrorHandler.Error_Context,
   --#         SPARK_IO.File_Sys          from CommandLineData.Content,
   --#                                         Dictionary.Dict,
   --#                                         ErrorHandler.Error_Context,
   --#                                         LexTokenManager.State,
   --#                                         Node,
   --#                                         SPARK_IO.File_Sys,
   --#                                         STree.Table,
   --#                                         Task_Sym;
   --# pre Syntax_Node_Type (Node, STree.Table) = SP_Symbols.declarative_part or
   --#   Syntax_Node_Type (Node, STree.Table) = SP_Symbols.sequence_of_statements;
   is
      Current_Node : STree.SyntaxNode;
      Last_Loop    : Dictionary.Symbol;

      function Last_Statement (Node : STree.SyntaxNode) return STree.SyntaxNode
      --# global in STree.Table;
      --# pre Syntax_Node_Type (Node, STree.Table) = SP_Symbols.declarative_part or
      --#   Syntax_Node_Type (Node, STree.Table) = SP_Symbols.sequence_of_statements;
      --# return Result => Syntax_Node_Type (Result, STree.Table) = SP_Symbols.statement;
      is
         Result : STree.SyntaxNode;
      begin
         Result := Node;
         if Syntax_Node_Type (Node => Result) = SP_Symbols.declarative_part then
            -- ASSUME Result = declarative_part
            Result := Next_Sibling (Current_Node => Result);
         end if;
         -- ASSUME Result = sequence_of_statements
         SystemErrors.RT_Assert
           (C       => Syntax_Node_Type (Node => Result) = SP_Symbols.sequence_of_statements,
            Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect Result = sequence_of_statements in Last_Statement");

         Result := Child_Node (Current_Node => Result);
         -- ASSUME Result = sequence_of_statements OR statement
         if Syntax_Node_Type (Node => Result) = SP_Symbols.sequence_of_statements then
            -- ASSUME Result = sequence_of_statements
            if Syntax_Node_Type (Node => Child_Node (Current_Node => Next_Sibling (Current_Node => Result))) =
              SP_Symbols.justification_statement then
               -- ASSUME Child_Node (Current_Node => Next_Sibling (Current_Node => Result)) = justification_statement
               Result := Child_Node (Current_Node => Result);
               -- ASSUME Result = sequence_of_statements OR statement
               SystemErrors.RT_Assert
                 (C       => Syntax_Node_Type (Node => Result) = SP_Symbols.sequence_of_statements
                    or else Syntax_Node_Type (Node => Result) = SP_Symbols.statement,
                  Sys_Err => SystemErrors.Invalid_Syntax_Tree,
                  Msg     => "Expect Result = sequence_of_statements OR statement in Last_Statement");
            end if;
         elsif Syntax_Node_Type (Node => Result) /= SP_Symbols.statement then
            Result := STree.NullNode;
            SystemErrors.Fatal_Error
              (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
               Msg     => "Expect Result = sequence_of_statements OR statement in Last_Statement");
         end if;
         --# check Syntax_Node_Type (Result, STree.Table) = SP_Symbols.sequence_of_statements or
         --#   Syntax_Node_Type (Result, STree.Table) = SP_Symbols.statement;
         if Syntax_Node_Type (Node => Result) = SP_Symbols.sequence_of_statements then
            -- ASSUME Result = sequence_of_statements
            Result := Next_Sibling (Current_Node => Result);
            -- ASSUME Result = statement
            SystemErrors.RT_Assert
              (C       => Syntax_Node_Type (Node => Result) = SP_Symbols.statement,
               Sys_Err => SystemErrors.Invalid_Syntax_Tree,
               Msg     => "Expect Result = statement in Last_Statement");
         end if;
         return Result;
      end Last_Statement;

   begin -- Check_For_Endless_Loop
      Current_Node := Last_Statement (Node => Node);
      --# check Syntax_Node_Type (Current_Node, STree.Table) = SP_Symbols.statement;
      Current_Node := Child_Node (Current_Node => Current_Node);
      -- ASSUME Current_Node = sequence_of_labels OR simple_statement OR compound_statement OR
      --                       proof_statement OR justification_statement OR apragma
      if Syntax_Node_Type (Node => Current_Node) = SP_Symbols.sequence_of_labels then
         -- ASSUME Current_Node = sequence_of_labels
         Current_Node := Next_Sibling (Current_Node => Current_Node);
      elsif Syntax_Node_Type (Node => Current_Node) /= SP_Symbols.simple_statement
        and then Syntax_Node_Type (Node => Current_Node) /= SP_Symbols.compound_statement
        and then Syntax_Node_Type (Node => Current_Node) /= SP_Symbols.proof_statement
        and then Syntax_Node_Type (Node => Current_Node) /= SP_Symbols.justification_statement
        and then Syntax_Node_Type (Node => Current_Node) /= SP_Symbols.apragma then
         SystemErrors.Fatal_Error
           (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect Current_Node = sequence_of_labels OR simple_statement OR compound_statement OR " &
              "proof_statement OR justification_statement OR apragma in Check_For_Endless_Loop");
      end if;
      -- ASSUME Current_Node = simple_statement OR compound_statement OR
      --                       proof_statement OR justification_statement OR apragma
      if Syntax_Node_Type (Node => Current_Node) = SP_Symbols.compound_statement then
         -- ASSUME Current_Node = compound_statement
         Current_Node := Child_Node (Current_Node => Current_Node);
         -- ASSUME Current_Node = if_statement OR case_statement OR loop_statement
         if Syntax_Node_Type (Node => Current_Node) = SP_Symbols.loop_statement then
            -- ASSUME Current_Node = loop_statement
            -- check loop has no exits
            Last_Loop := Dictionary.LastMostEnclosingLoop (Task_Sym);
            if not Dictionary.Is_Null_Symbol (Last_Loop) and then Dictionary.GetLoopHasExits (Last_Loop) then
               ErrorHandler.Semantic_Error
                 (Err_Num   => 989,
                  Reference => ErrorHandler.No_Reference,
                  Position  => Node_Position (Node => Last_Sibling_Of (Start_Node => Node)),
                  Id_Str    => LexTokenManager.Null_String);
            end if;
         elsif Syntax_Node_Type (Node => Current_Node) = SP_Symbols.if_statement
           or else Syntax_Node_Type (Node => Current_Node) = SP_Symbols.case_statement then
            -- ASSUME Current_Node = if_statement OR case_statement
            -- last statement not a loop
            ErrorHandler.Semantic_Error
              (Err_Num   => 989,
               Reference => ErrorHandler.No_Reference,
               Position  => Node_Position (Node => Last_Sibling_Of (Start_Node => Node)),
               Id_Str    => LexTokenManager.Null_String);
         else
            SystemErrors.Fatal_Error
              (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
               Msg     => "Expect Current_Node = if_statement OR case_statement OR loop_statement in Check_For_Endless_Loop");
         end if;
      elsif Syntax_Node_Type (Node => Current_Node) = SP_Symbols.simple_statement
        or else Syntax_Node_Type (Node => Current_Node) = SP_Symbols.proof_statement
        or else Syntax_Node_Type (Node => Current_Node) = SP_Symbols.justification_statement
        or else Syntax_Node_Type (Node => Current_Node) = SP_Symbols.apragma then
         -- ASSUME Current_Node = simple_statement OR proof_statement OR justification_statement OR apragma
         -- last statement not a loop
         ErrorHandler.Semantic_Error
           (Err_Num   => 989,
            Reference => ErrorHandler.No_Reference,
            Position  => Node_Position (Node => Last_Sibling_Of (Start_Node => Node)),
            Id_Str    => LexTokenManager.Null_String);
      else
         SystemErrors.Fatal_Error
           (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect Current_Node = simple_statement OR compound_statement OR " &
              "proof_statement OR justification_statement OR apragma in Check_For_Endless_Loop");
      end if;
   end Check_For_Endless_Loop;

begin -- Up_Wf_Task_Body
   Task_Sym := Dictionary.GetRegion (Scope);
   End_Node :=
     Next_Sibling
     (Current_Node => Child_Node
        (Current_Node => Next_Sibling (Current_Node => Next_Sibling (Current_Node => Child_Node (Current_Node => Node)))));
   -- ASSUME End_Node = declarative_part OR sequence_of_statements OR code_insertion OR hidden_part
   if Syntax_Node_Type (Node => End_Node) = SP_Symbols.declarative_part
     or else Syntax_Node_Type (Node => End_Node) = SP_Symbols.sequence_of_statements then
      -- ASSUME End_Node = declarative_part OR sequence_of_statements
      Check_For_Endless_Loop (Node     => End_Node,
                              Task_Sym => Task_Sym);
   elsif Syntax_Node_Type (Node => End_Node) /= SP_Symbols.code_insertion
     and then Syntax_Node_Type (Node => End_Node) /= SP_Symbols.hidden_part then
      SystemErrors.Fatal_Error
        (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Expect End_Node = declarative_part OR sequence_of_statements OR " &
           "code_insertion OR hidden_part in Up_Wf_Task_Body");
   end if;

   CheckSuspendsListAccountedFor (Proc_Or_Task => Task_Sym,
                                  Node_Pos     => Node_Position (Node => Node));

   -- step out to enclosing scope for continued tree walk
   Scope := Dictionary.GetEnclosingScope (Scope);
end Up_Wf_Task_Body;
