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

-- This procedure has essentially the functionality of WalkExpression
-- but is used to walk expressions which the language rules require to be a
-- "name".  In addition to the things returned by WalkExpression, a flag is
-- returned indicating whether the expression was indeed a name.
-- If the expression is not a name the expression is not traversed at all
-- and Result is set to the Unknown_Type_Record
--
-- After the name node has been found it is traversed by WalkExpression and a subset
-- of the checks usually done by wf_primary are acarried out on the result.  More
-- checks are done here than are necessary for this application (e.g. getting bounds
-- of a type mark) but they have been left in to make Walk_Name more widely applicable;
-- it could be moved to Wellformed if ever needed elsewhere.

separate (Sem)
procedure Walk_Name
  (Exp_Node       : in     STree.SyntaxNode;
   Scope          : in     Dictionary.Scopes;
   Component_Data : in out ComponentManager.ComponentData;
   The_Heap       : in out Heap.HeapRecord;
   Result         :    out Exp_Record;
   Is_A_Name      :    out Boolean;
   Ref_Var_Param  : in     SeqAlgebra.Seq)
is
   Node, Next_Node : STree.SyntaxNode;
   Sym             : Dictionary.Symbol;
begin
   -- preset result for all non-name cases advance to relation node;
   Result := Unknown_Type_Record;
   Node   := Child_Node (Current_Node => Exp_Node);
   -- ASSUME Node = relation
   SystemErrors.RT_Assert
     (C       => Syntax_Node_Type (Node => Node) = SP_Symbols.relation,
      Sys_Err => SystemErrors.Invalid_Syntax_Tree,
      Msg     => "Expect Node = relation in Walk_Name");
   Next_Node := Next_Sibling (Current_Node => Node);
   -- ASSUME Next_Node = expression_rep1 OR expression_rep2 OR expression_rep3 OR
   --                    expression_rep4 OR expression_rep5 OR NULL
   if Next_Node = STree.NullNode then
      -- ASSUME Next_Node = NULL
      -- advance to simple_expression;
      Node := Child_Node (Current_Node => Node);
      -- ASSUME Node = simple_expression
      SystemErrors.RT_Assert
        (C       => Syntax_Node_Type (Node => Node) = SP_Symbols.simple_expression,
         Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Expect Node = simple_expression in Walk_Name");
      Next_Node := Next_Sibling (Current_Node => Node);
      -- ASSUME Next_Node = relational_operator OR inside OR outside OR NULL
      if Next_Node = STree.NullNode then
         -- ASSUME Next_Node = NULL
         -- advance to simple_expression_opt;
         Node := Child_Node (Current_Node => Node);
         -- ASSUME Node = simple_expression OR simple_expression_opt
         if Syntax_Node_Type (Node => Node) = SP_Symbols.simple_expression_opt then
            -- Node = simple_expression_opt
            -- advance to term;
            Node := Child_Node (Current_Node => Node);
            -- ASSUME Node = unary_adding_operator OR term
            if Syntax_Node_Type (Node => Node) = SP_Symbols.term then
               -- ASSUME Node = term
               -- advance to factor;
               Node := Child_Node (Current_Node => Node);
               -- ASSUME Node = term OR factor
               if Syntax_Node_Type (Node => Node) = SP_Symbols.factor then
                  -- ASSUME Node = factor
                  -- advance to primary;
                  Node := Child_Node (Current_Node => Node);
                  -- ASSUME Node = primary OR RWabs OR RWnot
                  if Syntax_Node_Type (Node => Node) = SP_Symbols.primary then
                     -- ASSUME Node = primary
                     Next_Node := Next_Sibling (Current_Node => Node);
                     -- ASSUME Next_Node = double_star OR NULL
                     if Next_Node = STree.NullNode then
                        -- ASSUME Next_Node = NULL
                        -- advance to name;
                        Node := Child_Node (Current_Node => Node);
                        -- ASSUME Node = numeric_literal OR character_literal OR string_literal OR name OR
                        --               qualified_expression OR expression OR attribute
                        if Syntax_Node_Type (Node => Node) = SP_Symbols.name then
                           -- ASSUME Node = name
                           Is_A_Name := True;
                           Walk_Expression_P.Walk_Expression
                             (Exp_Node                => Node,
                              Scope                   => Scope,
                              Type_Context            => Dictionary.GetUnknownTypeMark,
                              Context_Requires_Static => False,
                              Ref_Var                 => Ref_Var_Param,
                              Result                  => Result,
                              Component_Data          => Component_Data,
                              The_Heap                => The_Heap);
                           -- now perform some checks on the result akin to those
                           -- of wf_primary
                           Sym := Result.Other_Symbol;
                           case Result.Sort is
                              when Is_Unknown =>
                                 Result := Unknown_Type_Record;
                              when Type_Result =>
                                 null;
                              when Is_Package =>
                                 Result := Unknown_Type_Record;
                                 ErrorHandler.Semantic_Error
                                   (Err_Num   => 5,
                                    Reference => ErrorHandler.No_Reference,
                                    Position  => Node_Position (Node => Node),
                                    Id_Str    => Dictionary.GetSimpleName (Sym));
                              when Is_Function =>
                                 ErrorHandler.Semantic_Error
                                   (Err_Num   => 3,
                                    Reference => ErrorHandler.No_Reference,
                                    Position  => Node_Position (Node => Node),
                                    Id_Str    => Dictionary.GetSimpleName (Sym));
                                 Result.Is_Static   := False;
                                 Result.Is_Constant := False;
                                 Result.Is_ARange   := False;
                              when Is_Object =>
                                 Result.Is_ARange := False;
                                 if Dictionary.IsVariableOrSubcomponent (Sym) then
                                    SeqAlgebra.AddMember (The_Heap, Ref_Var_Param, Natural (Dictionary.SymbolRef (Sym)));
                                 end if;
                              when Is_Type_Mark =>
                                 Result.Is_Static   := Dictionary.IsStatic (Sym, Scope);
                                 Result.Is_Constant := True;
                                 if Dictionary.IsScalarTypeMark (Sym, Scope) then -- we can get bounds of range and so on
                                    Result.Is_ARange := True;
                                    -- get upper and lower bounds of type from dictionary
                                    Result.Value     :=
                                      Maths.ValueRep
                                      (Dictionary.GetScalarAttributeValue (False, -- don't want base type
                                                                           LexTokenManager.First_Token, Sym));
                                    Result.Range_RHS :=
                                      Maths.ValueRep
                                      (Dictionary.GetScalarAttributeValue (False, -- don't want base type
                                                                           LexTokenManager.Last_Token, Sym));
                                 else -- not scalar so we don't do bounds and its not a range
                                    Result.Is_ARange := False;
                                 end if;
                              when Is_Parameter_Name =>
                                 null; -- should never occur
                           end case;
                           Result.Param_Count := 0;
                           Result.Param_List  := Lists.Null_List;
                        elsif Syntax_Node_Type (Node => Node) = SP_Symbols.numeric_literal
                          or else Syntax_Node_Type (Node => Node) = SP_Symbols.character_literal
                          or else Syntax_Node_Type (Node => Node) = SP_Symbols.string_literal
                          or else Syntax_Node_Type (Node => Node) = SP_Symbols.qualified_expression
                          or else Syntax_Node_Type (Node => Node) = SP_Symbols.expression
                          or else Syntax_Node_Type (Node => Node) = SP_Symbols.attribute then
                           -- ASSUME Node = numeric_literal OR character_literal OR string_literal OR
                           --               qualified_expression OR expression OR attribute
                           Is_A_Name := False;
                        else
                           Is_A_Name := False;
                           SystemErrors.Fatal_Error
                             (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
                              Msg     => "Expect Node = numeric_literal OR character_literal OR string_literal OR " &
                                "name OR qualified_expression OR expression OR attribute in Walk_Name");
                        end if;
                     elsif Syntax_Node_Type (Node => Next_Node) = SP_Symbols.double_star then
                        -- ASSUME Next_Node = double_star
                        Is_A_Name := False;
                     else
                        Is_A_Name := False;
                        SystemErrors.Fatal_Error
                          (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
                           Msg     => "Expect Next_Node = double_star OR NULL in Walk_Name");
                     end if;
                  elsif Syntax_Node_Type (Node => Node) = SP_Symbols.RWabs
                    or else Syntax_Node_Type (Node => Node) = SP_Symbols.RWnot then
                     -- ASSUME Node = RWabs OR RWnot
                     Is_A_Name := False;
                  else
                     Is_A_Name := False;
                     SystemErrors.Fatal_Error
                       (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
                        Msg     => "Expect Node = primary OR RWabs OR RWnot in Walk_Name");
                  end if;
               elsif Syntax_Node_Type (Node => Node) = SP_Symbols.term then
                  -- ASSUME Node = term
                  Is_A_Name := False;
               else
                  Is_A_Name := False;
                  SystemErrors.Fatal_Error
                    (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
                     Msg     => "Expect Node = term OR factor in Walk_Name");
               end if;
            elsif Syntax_Node_Type (Node => Node) = SP_Symbols.unary_adding_operator then
               -- ASSUME Node = unary_adding_operator
               Is_A_Name := False;
            else
               Is_A_Name := False;
               SystemErrors.Fatal_Error
                 (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
                  Msg     => "Expect Node = unary_adding_operator OR term in Walk_Name");
            end if;
         elsif Syntax_Node_Type (Node => Node) = SP_Symbols.simple_expression then
            -- ASSUME Node = simple_expression
            Is_A_Name := False;
         else
            Is_A_Name := False;
            SystemErrors.Fatal_Error
              (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
               Msg     => "Expect Node = simple_expression OR simple_expression_opt in Walk_Name");
         end if;
      elsif Syntax_Node_Type (Node => Next_Node) = SP_Symbols.relational_operator
        or else Syntax_Node_Type (Node => Next_Node) = SP_Symbols.inside
        or else Syntax_Node_Type (Node => Next_Node) = SP_Symbols.outside then
         -- ASSUME Next_Node = relational_operator OR inside OR outside
         Is_A_Name := False;
      else
         Is_A_Name := False;
         SystemErrors.Fatal_Error
           (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect Next_Node = relational_operator OR inside OR outside OR NULL in Walk_Name");
      end if;
   elsif Syntax_Node_Type (Node => Next_Node) = SP_Symbols.expression_rep1
     or else Syntax_Node_Type (Node => Next_Node) = SP_Symbols.expression_rep2
     or else Syntax_Node_Type (Node => Next_Node) = SP_Symbols.expression_rep3
     or else Syntax_Node_Type (Node => Next_Node) = SP_Symbols.expression_rep4
     or else Syntax_Node_Type (Node => Next_Node) = SP_Symbols.expression_rep5 then
      -- ASSUME Next_Node = expression_rep1 OR expression_rep2 OR expression_rep3 OR
      --                    expression_rep4 OR expression_rep5
      Is_A_Name := False;
   else
      Is_A_Name := False;
      SystemErrors.Fatal_Error
        (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Expect Next_Node = expression_rep1 OR expression_rep2 OR expression_rep3 OR " &
           "expression_rep4 OR expression_rep5 OR NULL in Walk_Name");
   end if;
end Walk_Name;
