-------------------------------------------------------------------------------
-- (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)
procedure Wf_Entire_Variable
  (Node       : in     STree.SyntaxNode;
   Scope      : in     Dictionary.Scopes;
   Error_Hint : in     Visibility_Error_Hint;
   Var_Sym    :    out Dictionary.Symbol;
   Dotted     :    out Boolean)
is
   Id_Node          : STree.SyntaxNode;
   It               : STree.Iterator;
   P_Id_Str, Id_Str : LexTokenManager.Lex_String;
   Sym_So_Far       : Dictionary.Symbol;
   Prefix_Ok        : Boolean;
begin
   -- Node is the root of any variable (dotted or otherwise).

   P_Id_Str := LexTokenManager.Null_String;
   Var_Sym  := Dictionary.NullSymbol;

   -- This procedure traverses (depth first) the tree under the root looking for
   -- identifiers. Var_Sym is made up of all the identifiers found.
   It := Find_First_Node (Node_Kind    => SP_Symbols.identifier,
                          From_Root    => Node,
                          In_Direction => STree.Down);
   --# check Syntax_Node_Type (Get_Node (It), STree.Table) = SP_Symbols.identifier or
   --#   STree.IsNull (It);
   if not STree.IsNull (It => It) then
      -- ASSUME It = identifier
      Id_Node := Get_Node (It => It);
      --# check Syntax_Node_Type (Id_Node, STree.Table) = SP_Symbols.identifier;
      Id_Str  := Node_Lex_String (Node => Id_Node);
      Var_Sym :=
        Dictionary.LookupItem (Name              => Id_Str,
                               Scope             => Scope,
                               Context           => Dictionary.ProofContext,
                               Full_Package_Name => False);
      loop
         --# assert STree.Table = STree.Table~ and
         --#   (Syntax_Node_Type (Node, STree.Table) = SP_Symbols.entire_variable or
         --#      Syntax_Node_Type (Node, STree.Table) = SP_Symbols.annotation_primary) and
         --#   Syntax_Node_Type (Id_Node, STree.Table) = SP_Symbols.identifier and
         --#   Id_Node = Get_Node (It);
         if Dictionary.Is_Null_Symbol (Var_Sym) then
            case Error_Hint is
               when No_Hint =>
                  ErrorHandler.Semantic_Error2
                    (Err_Num   => 1,
                     Reference => ErrorHandler.No_Reference,
                     Position  => Node_Position (Node => Node),
                     Id_Str1   => Id_Str,
                     Id_Str2   => P_Id_Str);
               when In_Global_List =>
                  ErrorHandler.Semantic_Error2
                    (Err_Num   => 144,
                     Reference => ErrorHandler.No_Reference,
                     Position  => Node_Position (Node => Node),
                     Id_Str1   => Id_Str,
                     Id_Str2   => P_Id_Str);
               when In_Derives_Import_List =>
                  ErrorHandler.Semantic_Error2
                    (Err_Num   => 752,
                     Reference => ErrorHandler.No_Reference,
                     Position  => Node_Position (Node => Node),
                     Id_Str1   => Id_Str,
                     Id_Str2   => P_Id_Str);
               when In_Derives_Export_List =>
                  ErrorHandler.Semantic_Error2
                    (Err_Num   => 753,
                     Reference => ErrorHandler.No_Reference,
                     Position  => Node_Position (Node => Node),
                     Id_Str1   => Id_Str,
                     Id_Str2   => P_Id_Str);
               when In_Suspends_List =>
                  ErrorHandler.Semantic_Error2
                    (Err_Num   => 755,
                     Reference => ErrorHandler.No_Reference,
                     Position  => Node_Position (Node => Node),
                     Id_Str1   => Id_Str,
                     Id_Str2   => P_Id_Str);
            end case;
            exit;
         end if;

         It := STree.NextNode (It);
         --# check Syntax_Node_Type (Get_Node (It), STree.Table) = SP_Symbols.identifier or
         --#   STree.IsNull (It);
         if Dictionary.Is_Variable (Var_Sym) then
            -- at this point Sym is a variable, final check that there is no dotted
            -- part to the right of it as there would be if a record field was there
            if STree.IsNull (It => It) then
               -- ASSUME It = NULL
               STree.Set_Node_Lex_String (Sym  => Var_Sym,
                                          Node => Id_Node);
            elsif Syntax_Node_Type (Node => Get_Node (It => It)) = SP_Symbols.identifier then
               -- ASSUME It = identifier
               ErrorHandler.Semantic_Error
                 (Err_Num   => 156,
                  Reference => ErrorHandler.No_Reference,
                  Position  => Node_Position (Node => Node),
                  Id_Str    => LexTokenManager.Null_String);
               Var_Sym := Dictionary.NullSymbol;
            end if;
            exit;
         end if;

         if Dictionary.Is_Constant (Var_Sym) then
            ErrorHandler.Semantic_Error
              (Err_Num   => 150,
               Reference => ErrorHandler.No_Reference,
               Position  => Node_Position (Node => Node),
               Id_Str    => LexTokenManager.Null_String);
            Var_Sym := Dictionary.NullSymbol;
            exit;
         end if;

         if not Dictionary.IsPackage (Var_Sym) then
            ErrorHandler.Semantic_Error
              (Err_Num   => 156,
               Reference => ErrorHandler.No_Reference,
               Position  => Node_Position (Node => Node),
               Id_Str    => LexTokenManager.Null_String);
            Var_Sym := Dictionary.NullSymbol;
            exit;
         end if;

         if STree.IsNull (It => It) then
            -- ASSUME It = NULL
            -- package without a selected component
            ErrorHandler.Semantic_Error
              (Err_Num   => 156,
               Reference => ErrorHandler.No_Reference,
               Position  => Node_Position (Node => Node),
               Id_Str    => LexTokenManager.Null_String);
            Var_Sym := Dictionary.NullSymbol;
            exit;
         end if;

         Check_Package_Prefix (Node_Pos => Node_Position (Node => Id_Node),
                               Pack_Sym => Var_Sym,
                               Scope    => Scope,
                               OK       => Prefix_Ok);
         if not Prefix_Ok then
            Var_Sym := Dictionary.NullSymbol;
            exit;
         end if;
         STree.Set_Node_Lex_String (Sym  => Var_Sym,
                                    Node => Id_Node);
         P_Id_Str   := Id_Str;
         Id_Node    := Get_Node (It => It);
         Id_Str     := Node_Lex_String (Node => Id_Node);
         Sym_So_Far := Var_Sym;
         Var_Sym    :=
           Dictionary.LookupSelectedItem
           (Prefix   => Var_Sym,
            Selector => Id_Str,
            Scope    => Scope,
            Context  => Dictionary.ProofContext);
         -- check to see if we are getting the same symbol over and again
         if Var_Sym = Sym_So_Far then -- P.P.P.P.X case
            Var_Sym := Dictionary.NullSymbol; -- to cause "Not visible" error at top of loop
         end if;
      end loop;
   end if;

   Dotted :=
     LexTokenManager.Lex_String_Case_Insensitive_Compare (Lex_Str1 => P_Id_Str,
                                                          Lex_Str2 => LexTokenManager.Null_String) /=
     LexTokenManager.Str_Eq
     and then not Dictionary.Is_Null_Symbol (Var_Sym);
end Wf_Entire_Variable;
