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

with CommandLineData;
with E_Strings.Not_SPARK;
with SparkLex;
with SPARK_IO;
with SP_Symbols;
with SparkMakeDebug;
with SparkMakeErrors;
with TokenManager;

use type SP_Symbols.SP_Terminal;
use type SPARK_IO.File_Status;
use type TokenManager.Token;
use type CommandLineData.Language_Profiles;

package body Unit is

   function Kind_To_String (The_Unit : Kind) return E_Strings.T is
      Result : E_Strings.T;
   begin
      case The_Unit is

         when Specification_Unit =>
            Result := E_Strings.Copy_String (Str => "specification");

         when Package_Body_Unit =>
            Result := E_Strings.Copy_String (Str => "body");

         when Separate_Body_Unit | Main_Program_Unit =>
            Result := E_Strings.Copy_String (Str => "subunit");

      end case;
      return Result;
   end Kind_To_String;

   -----------------------------------------------------------------------------

   function Are_Equal (L, R : Id) return Boolean is
   begin
      return E_Strings.Eq_String (E_Str1 => L.The_Name,
                                  E_Str2 => R.The_Name) and then L.The_Kind = R.The_Kind;
   end Are_Equal;

   ------------------------------------------------------------------------------

   function Less_Than (L, R : Id) return Boolean is
      Result    : Boolean;
      The_Order : E_Strings.Order_Types;
   begin
      The_Order := E_Strings.Lex_Order (First_Name  => L.The_Name,
                                        Second_Name => R.The_Name);
      case The_Order is
         when E_Strings.First_One_First =>
            Result := True;
         when E_Strings.Second_One_First =>
            Result := False;
         when E_Strings.Neither_First =>
            Result := L.The_Kind < R.The_Kind;
      end case;
      return Result;
   end Less_Than;

   ------------------------------------------------------------------------------

   procedure Open (The_Filename : in     E_Strings.T;
                   The_File_Id  :    out SPARK_IO.File_Type;
                   Success      :    out Boolean)
   --# global in out SPARK_IO.File_Sys;
   --# derives SPARK_IO.File_Sys,
   --#         Success,
   --#         The_File_Id       from SPARK_IO.File_Sys,
   --#                                The_Filename;
   is
      Status : SPARK_IO.File_Status;
   begin
      The_File_Id := SPARK_IO.Null_File;
      E_Strings.Open
        (File         => The_File_Id,
         Mode_Of_File => SPARK_IO.In_File,
         Name_Of_File => The_Filename,
         Form_Of_File => "",
         Status       => Status);
      Success := Status = SPARK_IO.Ok;
   end Open;

   -----------------------------------------------------------------------------

   function Prefix (E_Str : E_Strings.T) return E_Strings.T
   -- Returns the text up to but not including the final dot.
   -- E.g. Prefix (A.B.C.D) returns A.B.C
   is
      Char_Found : Boolean;
      Char_Pos   : E_Strings.Positions;
      Last_Pos   : E_Strings.Lengths := 0;
      Result     : E_Strings.T;
   begin
      -- Look for the last dot.
      loop
         E_Strings.Find_Char_After
           (E_Str        => E_Str,
            Search_Start => Last_Pos + 1,
            Search_Char  => '.',
            Char_Found   => Char_Found,
            Char_Pos     => Char_Pos);

         exit when not Char_Found;

         if Char_Found then
            Last_Pos := Char_Pos;
         end if;
      end loop;

      -- Last Pos contains the position of the last dot.

      if Last_Pos = 0 then
         -- There was not dot so return empty string.
         Result := E_Strings.Empty_String;
      else
         -- Return the text up to but not including the last dot.
         Result := E_Strings.Section (E_Str     => E_Str,
                                      Start_Pos => 1,
                                      Length    => Last_Pos - 1);
      end if;
      return Result;
   end Prefix;

   -----------------------------------------------------------------------------

   function Construct_Spec_Unit_Id (The_Name   : E_Strings.T;
                                    Is_Private : Boolean) return Id is
      The_Result : Id;
   begin
      if E_Strings.Is_Empty (E_Str => Prefix (The_Name)) then
         The_Result := Id'(The_Name => The_Name,
                           The_Kind => Package_Specification_Unit);
      elsif Is_Private then
         The_Result := Id'(The_Name => The_Name,
                           The_Kind => Private_Child_Package_Specification_Unit);
      else
         The_Result := Id'(The_Name => The_Name,
                           The_Kind => Public_Child_Package_Specification_Unit);
      end if;
      return The_Result;
   end Construct_Spec_Unit_Id;

   -----------------------------------------------------------------------------

   procedure Get_Unit (In_File  : in     E_Strings.T;
                       The_Unit :    out Object) is

      Token_It      : TokenManager.Iterator;
      Current_Token : TokenManager.Token;
      The_File_Id   : SPARK_IO.File_Type;
      Status        : SPARK_IO.File_Status;
      Success       : Boolean;
      Done          : Boolean := False;

      ------------------------------------------------------------------

      procedure Output_Unexpected_Token (Expected : in SP_Symbols.SP_Symbol;
                                         Found    : in TokenManager.Token)
      --# derives null from Expected,
      --#                   Found;
      is
         --# hide Output_Unexpected_Token;
         E_Str : E_Strings.T;
      begin
         E_Str := TokenManager.To_String (Tok => Found);
         SparkMakeDebug.Report_Cond_Text
           (Cond       => Expected /= SP_Symbols.SPEND,
            True_Text  => "Unexpected token. " &
              "Expecting " &
              SP_Symbols.SP_Symbol'Image (Expected) &
              ", " &
              "but found " &
              E_Strings.Not_SPARK.Get_String (E_Str => E_Str),
            False_Text => "Unexpected token. " & "Found " & E_Strings.Not_SPARK.Get_String (E_Str => E_Str));
      end Output_Unexpected_Token;

      ------------------------------------------------------------------

      procedure Assert_Next_Token (Is_Symbol : in SP_Symbols.SP_Symbol)
      --# global in     CommandLineData.Content;
      --#        in     Dictionary.Dict;
      --#        in out ErrorHandler.Error_Context;
      --#        in out LexTokenManager.State;
      --#        in out SparkLex.Curr_Line;
      --#        in out SPARK_IO.File_Sys;
      --#        in out Token_It;
      --#           out Success;
      --# derives ErrorHandler.Error_Context,
      --#         LexTokenManager.State,
      --#         SparkLex.Curr_Line,
      --#         SPARK_IO.File_Sys,
      --#         Token_It                   from CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It &
      --#         Success                    from CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         Is_Symbol,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It;
      is
         Current_Token : TokenManager.Token;
      begin
         -- Get the next token.
         TokenManager.Next (It => Token_It);
         Current_Token := TokenManager.Current (Token_It);
         -- Is it what we were expecting?
         Success := Current_Token.Kind = Is_Symbol;
         if not Success then
            Output_Unexpected_Token (Expected => Is_Symbol,
                                     Found    => Current_Token);
         end if;
      end Assert_Next_Token;

      ------------------------------------------------------------------

      procedure Get_And_Assert_Next_Token (Is_Symbol     : in     SP_Symbols.SP_Symbol;
                                           Current_Token :    out TokenManager.Token)
      --# global in     CommandLineData.Content;
      --#        in     Dictionary.Dict;
      --#        in out ErrorHandler.Error_Context;
      --#        in out LexTokenManager.State;
      --#        in out SparkLex.Curr_Line;
      --#        in out SPARK_IO.File_Sys;
      --#        in out Token_It;
      --#           out Success;
      --# derives Current_Token,
      --#         Success                    from CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         Is_Symbol,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It &
      --#         ErrorHandler.Error_Context,
      --#         LexTokenManager.State,
      --#         SparkLex.Curr_Line,
      --#         SPARK_IO.File_Sys,
      --#         Token_It                   from CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It;
      is
      begin
         Assert_Next_Token (Is_Symbol => Is_Symbol);
         if Success then
            Current_Token := TokenManager.Current (Token_It);
         else
            Current_Token := TokenManager.Null_Token;
         end if;
      end Get_And_Assert_Next_Token;

      ------------------------------------------------------------------

      procedure Process_With_Clause
      --# global in     CommandLineData.Content;
      --#        in     Dictionary.Dict;
      --#        in out ErrorHandler.Error_Context;
      --#        in out LexTokenManager.State;
      --#        in out SparkLex.Curr_Line;
      --#        in out SPARK_IO.File_Sys;
      --#        in out The_Unit;
      --#        in out Token_It;
      --#           out Success;
      --# derives ErrorHandler.Error_Context,
      --#         LexTokenManager.State,
      --#         SparkLex.Curr_Line,
      --#         SPARK_IO.File_Sys,
      --#         The_Unit,
      --#         Token_It                   from *,
      --#                                         CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It &
      --#         Success                    from CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It;
      is
         Current_Token : TokenManager.Token;
      begin
         SparkMakeDebug.Report_Text (Text => "found with clause");
         Get_And_Assert_Next_Token (Is_Symbol     => SP_Symbols.identifier,
                                    Current_Token => Current_Token);
         while Success loop
            -- Add this unit to the list of withed units
            StringList.Add_To_Front (To_List  => The_Unit.The_Withed_Units,
                                     The_Item => Current_Token.Value);
            -- What's next?
            TokenManager.Next (It => Token_It);
            Current_Token := TokenManager.Current (Token_It);
            -- semicolon marks the end of the with clause.
            exit when Current_Token.Kind = SP_Symbols.semicolon;
            -- otherwise we must have a comma.
            Success := Current_Token.Kind = SP_Symbols.comma;
            if Success then
               Get_And_Assert_Next_Token (Is_Symbol     => SP_Symbols.identifier,
                                          Current_Token => Current_Token);
            else
               Output_Unexpected_Token (Expected => SP_Symbols.comma,
                                        Found    => Current_Token);
            end if;
         end loop;
         SparkMakeDebug.Report_List (Text => "The withed units are: ",
                                     List => The_Unit.The_Withed_Units);
      end Process_With_Clause;

      ------------------------------------------------------------------

      procedure Process_Inherit_Clause
      --# global in     CommandLineData.Content;
      --#        in     Dictionary.Dict;
      --#        in out ErrorHandler.Error_Context;
      --#        in out LexTokenManager.State;
      --#        in out SparkLex.Curr_Line;
      --#        in out SPARK_IO.File_Sys;
      --#        in out The_Unit;
      --#        in out Token_It;
      --#           out Success;
      --# derives ErrorHandler.Error_Context,
      --#         LexTokenManager.State,
      --#         SparkLex.Curr_Line,
      --#         SPARK_IO.File_Sys,
      --#         The_Unit,
      --#         Token_It                   from *,
      --#                                         CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It &
      --#         Success                    from CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It;
      is
         Current_Token : TokenManager.Token;
      begin
         SparkMakeDebug.Report_Text (Text => "found inherit clause");
         Get_And_Assert_Next_Token (Is_Symbol     => SP_Symbols.identifier,
                                    Current_Token => Current_Token);
         while Success loop
            -- Add this unit to the list of inherited units
            StringList.Add_To_Front (To_List  => The_Unit.The_Inherited_Units,
                                     The_Item => Current_Token.Value);
            -- What's next?
            TokenManager.Next (It => Token_It);
            Current_Token := TokenManager.Current (Token_It);
            -- semicolon marks the end of the inherit clause.
            if Current_Token.Kind = SP_Symbols.semicolon then
               Assert_Next_Token (Is_Symbol => SP_Symbols.annotation_end);
               exit;
            end if;
            -- otherwise we must have a comma.
            Success := Current_Token.Kind = SP_Symbols.comma;
            if Success then
               Get_And_Assert_Next_Token (Is_Symbol     => SP_Symbols.identifier,
                                          Current_Token => Current_Token);
            else
               Output_Unexpected_Token (Expected => SP_Symbols.comma,
                                        Found    => Current_Token);
            end if;
         end loop;
         SparkMakeDebug.Report_List (Text => "The inherited units are: ",
                                     List => The_Unit.The_Inherited_Units);
      end Process_Inherit_Clause;

      ------------------------------------------------------------------

      procedure Process_Package_Specification (Is_Private : in Boolean)
      --# global in     CommandLineData.Content;
      --#        in     Dictionary.Dict;
      --#        in out ErrorHandler.Error_Context;
      --#        in out LexTokenManager.State;
      --#        in out SparkLex.Curr_Line;
      --#        in out SPARK_IO.File_Sys;
      --#        in out The_Unit;
      --#        in out Token_It;
      --#           out Done;
      --#           out Success;
      --# derives Done                       from  &
      --#         ErrorHandler.Error_Context,
      --#         LexTokenManager.State,
      --#         SparkLex.Curr_Line,
      --#         SPARK_IO.File_Sys,
      --#         Success,
      --#         Token_It                   from CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It &
      --#         The_Unit                   from *,
      --#                                         CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         Is_Private,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It;
      is
         Current_Token : TokenManager.Token;
      begin
         SparkMakeDebug.Report_Cond_Text
           (Cond       => Is_Private,
            True_Text  => "found private package specification",
            False_Text => "found package specification");
         -- Get the package name
         Get_And_Assert_Next_Token (Is_Symbol     => SP_Symbols.identifier,
                                    Current_Token => Current_Token);
         if Success then
            SparkMakeDebug.Report_Text_E_Text (Text   => "with name ",
                                               E_Text => Current_Token.Value);
            The_Unit.The_Id := Construct_Spec_Unit_Id (The_Name   => Current_Token.Value,
                                                       Is_Private => Is_Private);
         end if;
         Done := True;
      end Process_Package_Specification;

      ------------------------------------------------------------------

      procedure Process_Package_Body
      --# global in     CommandLineData.Content;
      --#        in     Dictionary.Dict;
      --#        in out ErrorHandler.Error_Context;
      --#        in out LexTokenManager.State;
      --#        in out SparkLex.Curr_Line;
      --#        in out SPARK_IO.File_Sys;
      --#        in out The_Unit;
      --#        in out Token_It;
      --#           out Done;
      --#           out Success;
      --# derives Done                       from  &
      --#         ErrorHandler.Error_Context,
      --#         LexTokenManager.State,
      --#         SparkLex.Curr_Line,
      --#         SPARK_IO.File_Sys,
      --#         The_Unit,
      --#         Token_It                   from *,
      --#                                         CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It &
      --#         Success                    from CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It;
      is
         Current_Token : TokenManager.Token;
      begin
         SparkMakeDebug.Report_Text (Text => "found package body");
         -- Get the package name.
         Get_And_Assert_Next_Token (Is_Symbol     => SP_Symbols.identifier,
                                    Current_Token => Current_Token);
         if Success then
            SparkMakeDebug.Report_Text_E_Text (Text   => "with name ",
                                               E_Text => Current_Token.Value);
            The_Unit.The_Id := Id'(The_Name => Current_Token.Value,
                                   The_Kind => Package_Body_Unit);
         end if;
         Done := True;
      end Process_Package_Body;

      ------------------------------------------------------------------

      procedure Process_Package
      --# global in     CommandLineData.Content;
      --#        in     Dictionary.Dict;
      --#        in out ErrorHandler.Error_Context;
      --#        in out LexTokenManager.State;
      --#        in out SparkLex.Curr_Line;
      --#        in out SPARK_IO.File_Sys;
      --#        in out The_Unit;
      --#        in out Token_It;
      --#           out Done;
      --#           out Success;
      --# derives Done,
      --#         ErrorHandler.Error_Context,
      --#         LexTokenManager.State,
      --#         SparkLex.Curr_Line,
      --#         SPARK_IO.File_Sys,
      --#         Success,
      --#         Token_It                   from CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It &
      --#         The_Unit                   from *,
      --#                                         CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It;
      is
         Current_Token : TokenManager.Token;
      begin
         TokenManager.Look_Ahead (It => Token_It);
         Current_Token := TokenManager.Current (Token_It);
         -- Is this a package spec or body?
         if Current_Token.Kind = SP_Symbols.RWbody then
            -- Read over the reserved word body
            TokenManager.Next (It => Token_It);
            Process_Package_Body;
         else
            Process_Package_Specification (Is_Private => False);
         end if;
      end Process_Package;

      ------------------------------------------------------------------

      procedure Process_Private_Child_Specification
      --# global in     CommandLineData.Content;
      --#        in     Dictionary.Dict;
      --#        in out Done;
      --#        in out ErrorHandler.Error_Context;
      --#        in out LexTokenManager.State;
      --#        in out SparkLex.Curr_Line;
      --#        in out SPARK_IO.File_Sys;
      --#        in out The_Unit;
      --#        in out Token_It;
      --#           out Success;
      --# derives Done,
      --#         ErrorHandler.Error_Context,
      --#         LexTokenManager.State,
      --#         SparkLex.Curr_Line,
      --#         SPARK_IO.File_Sys,
      --#         The_Unit,
      --#         Token_It                   from *,
      --#                                         CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It &
      --#         Success                    from CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It;
      is
      begin
         SparkMakeDebug.Report_Text (Text => "found reserved word private");
         -- Must be reserved word package
         Assert_Next_Token (Is_Symbol => SP_Symbols.RWpackage);
         if Success then
            Process_Package_Specification (Is_Private => True);
         end if;
      end Process_Private_Child_Specification;

      ------------------------------------------------------------------

      procedure Process_Separate
      --# global in     CommandLineData.Content;
      --#        in     Dictionary.Dict;
      --#        in out ErrorHandler.Error_Context;
      --#        in out LexTokenManager.State;
      --#        in out SparkLex.Curr_Line;
      --#        in out SPARK_IO.File_Sys;
      --#        in out The_Unit;
      --#        in out Token_It;
      --#           out Done;
      --#           out Success;
      --# derives Done                       from  &
      --#         ErrorHandler.Error_Context,
      --#         LexTokenManager.State,
      --#         SparkLex.Curr_Line,
      --#         SPARK_IO.File_Sys,
      --#         The_Unit,
      --#         Token_It                   from *,
      --#                                         CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It &
      --#         Success                    from CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It;
      is
         Current_Token : TokenManager.Token;
      begin
         SparkMakeDebug.Report_Text (Text => "found separate");
         Assert_Next_Token (Is_Symbol => SP_Symbols.left_paren);
         if Success then
            -- Get the parent name.
            Get_And_Assert_Next_Token (Is_Symbol     => SP_Symbols.identifier,
                                       Current_Token => Current_Token);
            if Success then
               SparkMakeDebug.Report_Text_E_Text (Text   => "of enclosing unit ",
                                                  E_Text => Current_Token.Value);
               The_Unit.The_Id.The_Kind := Separate_Body_Unit;

               -- The parent
               The_Unit.The_Id.The_Name := Current_Token.Value;

               Assert_Next_Token (Is_Symbol => SP_Symbols.right_paren);

               if Success then
                  TokenManager.Next (It => Token_It);
                  Current_Token := TokenManager.Current (Token_It);

                  -- If language is SPARK 2005 then allow for an optional overriding_indicator
                  if CommandLineData.Content.Language_Profile = CommandLineData.SPARK2005 then
                     if Current_Token.Kind = SP_Symbols.RWnot then
                        Assert_Next_Token (Is_Symbol => SP_Symbols.RWoverriding);
                        TokenManager.Next (Token_It);
                        Current_Token := TokenManager.Current (Token_It);
                     elsif Current_Token.Kind = SP_Symbols.RWoverriding then
                        TokenManager.Next (Token_It);
                        Current_Token := TokenManager.Current (Token_It);
                     end if;
                  end if;

                  -- Is this a procedure, function, package or task?
                  if Current_Token.Kind = SP_Symbols.RWpackage or Current_Token.Kind = SP_Symbols.RWtask then
                     Assert_Next_Token (Is_Symbol => SP_Symbols.RWbody);
                  elsif Current_Token.Kind /= SP_Symbols.RWprocedure and Current_Token.Kind /= SP_Symbols.RWfunction then
                     Success := False;
                     Output_Unexpected_Token (Expected => SP_Symbols.SPEND,
                                              Found    => Current_Token);
                  end if;

                  if Success then
                     -- Get the unit name.
                     Get_And_Assert_Next_Token (Is_Symbol     => SP_Symbols.identifier,
                                                Current_Token => Current_Token);
                     if Success then
                        E_Strings.Append_String (E_Str => The_Unit.The_Id.The_Name,
                                                 Str   => ".");
                        E_Strings.Append_Examiner_String (E_Str1 => The_Unit.The_Id.The_Name,
                                                          E_Str2 => Current_Token.Value);
                        SparkMakeDebug.Report_Text_E_Text (Text   => "Full unit name is ",
                                                           E_Text => The_Unit.The_Id.The_Name);
                     end if;
                  end if;
               end if;
            end if;
         end if;
         Done := True;
      end Process_Separate;

      ------------------------------------------------------------------

      procedure Process_Main_Program
      --# global in     CommandLineData.Content;
      --#        in     Dictionary.Dict;
      --#        in out ErrorHandler.Error_Context;
      --#        in out LexTokenManager.State;
      --#        in out SparkLex.Curr_Line;
      --#        in out SPARK_IO.File_Sys;
      --#        in out The_Unit;
      --#        in out Token_It;
      --#           out Done;
      --#           out Success;
      --# derives Done                       from  &
      --#         ErrorHandler.Error_Context,
      --#         LexTokenManager.State,
      --#         SparkLex.Curr_Line,
      --#         SPARK_IO.File_Sys,
      --#         The_Unit,
      --#         Token_It                   from *,
      --#                                         CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It &
      --#         Success                    from CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It;
      is
         Current_Token : TokenManager.Token;
      begin
         SparkMakeDebug.Report_Text (Text => "found main program");
         loop
            TokenManager.Next (It => Token_It);
            Current_Token := TokenManager.Current (Token_It);
            exit when Current_Token.Kind = SP_Symbols.SPEND or Current_Token.Kind = SP_Symbols.RWprocedure;
         end loop;
         if Current_Token.Kind = SP_Symbols.RWprocedure then
            -- Get the main program name.
            Get_And_Assert_Next_Token (Is_Symbol     => SP_Symbols.identifier,
                                       Current_Token => Current_Token);
            if Success then
               SparkMakeDebug.Report_Text_E_Text (Text   => "main program is ",
                                                  E_Text => Current_Token.Value);
               The_Unit.The_Id := Id'(The_Name => Current_Token.Value,
                                      The_Kind => Main_Program_Unit);
            end if;
         else
            Success := False;
            Output_Unexpected_Token (Expected => SP_Symbols.RWprocedure,
                                     Found    => Current_Token);
         end if;
         Done := True;
      end Process_Main_Program;

      ------------------------------------------------------------------

      procedure Process_Annotation
      --# global in     CommandLineData.Content;
      --#        in     Dictionary.Dict;
      --#        in out Done;
      --#        in out ErrorHandler.Error_Context;
      --#        in out LexTokenManager.State;
      --#        in out SparkLex.Curr_Line;
      --#        in out SPARK_IO.File_Sys;
      --#        in out Success;
      --#        in out The_Unit;
      --#        in out Token_It;
      --# derives Done,
      --#         Success,
      --#         The_Unit                   from *,
      --#                                         CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Token_It &
      --#         ErrorHandler.Error_Context,
      --#         LexTokenManager.State,
      --#         SparkLex.Curr_Line,
      --#         SPARK_IO.File_Sys,
      --#         Token_It                   from CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Success,
      --#                                         Token_It;
      is
         Current_Token : TokenManager.Token;
      begin
         SparkMakeDebug.Report_Text (Text => "found annotation");
         TokenManager.Next (It => Token_It);
         Current_Token := TokenManager.Current (Token_It);
         -- Is this the main program
         if Current_Token.Kind = SP_Symbols.RWmain_program then
            Process_Main_Program;
         elsif Current_Token.Kind = SP_Symbols.RWinherit then
            Process_Inherit_Clause;
         else
            -- read until annotation end
            while Success loop
               TokenManager.Next (It => Token_It);
               Current_Token := TokenManager.Current (Token_It);
               exit when Current_Token.Kind = SP_Symbols.annotation_end;
               if Current_Token.Kind = SP_Symbols.SPEND then
                  Success := False;
               end if;
            end loop;
         end if;
      end Process_Annotation;

      ------------------------------------------------------------------

   begin
      SparkLex.Clear_Line_Context;
      The_Unit := Null_Object;

      Open (The_Filename => In_File,
            The_File_Id  => The_File_Id,
            Success      => Success);

      if Success then

         SparkMakeDebug.Report_Text_E_Text (Text   => "Parsing file ",
                                            E_Text => In_File);

         TokenManager.Get_First_Token (File_Id => The_File_Id,
                                       It      => Token_It);

         if TokenManager.Is_Null (It => Token_It) then
            Success := False;
         else
            while Success and then not Done loop

               Current_Token := TokenManager.Current (Token_It);

               case Current_Token.Kind is

                  when SP_Symbols.RWwith =>

                     Process_With_Clause;

                  when SP_Symbols.RWprivate =>

                     Process_Private_Child_Specification;

                  when SP_Symbols.RWpackage =>

                     Process_Package;

                  when SP_Symbols.RWseparate =>

                     Process_Separate;

                  when SP_Symbols.annotation_start =>

                     Process_Annotation;

                  when others =>

                     -- ignore these tokens
                     null;

               end case;

               if TokenManager.Is_Null (It => Token_It) then
                  Success := False;
               else
                  TokenManager.Next (It => Token_It);
               end if;

            end loop;
         end if;

         --# accept Flow, 10, The_File_Id, "Closed file handle not used";
         SPARK_IO.Close (File   => The_File_Id,
                         Status => Status);
         --# end accept;

         if Status /= SPARK_IO.Ok then
            SparkMakeErrors.Fatal ("Cannot close file");
         end if;
      else
         E_Strings.Put_Line (File  => SPARK_IO.Standard_Output,
                             E_Str => In_File);
         SparkMakeErrors.Fatal ("Cannot open file");
      end if;
      if Success then
         The_Unit.The_File := In_File;
      else
         The_Unit := Null_Object;
      end if;
   end Get_Unit;

   -----------------------------------------------------------------------------

   procedure Output_Object (The_Unit : in Object) is
      --# hide Output_Object;
   begin
      SparkMakeDebug.Report_Text (Text => "Unit debug:");
      SparkMakeDebug.Report_Cond_Text
        (Cond       => The_Unit = Null_Object,
         True_Text  => "Object is null",
         False_Text => "Object details below");
      if The_Unit /= Null_Object then
         SparkMakeDebug.Report_Text_E_Text (Text   => "File: ",
                                            E_Text => The_Unit.The_File);
         SparkMakeDebug.Report_Text_E_Text (Text   => "Name: ",
                                            E_Text => The_Unit.The_Id.The_Name);
         SparkMakeDebug.Report_Text (Text => "Kind: " & Kind'Image (The_Unit.The_Id.The_Kind));
         SparkMakeDebug.Report_List (Text => "The withed units: ",
                                     List => The_Unit.The_Withed_Units);
         SparkMakeDebug.Report_List (Text => "The inherited units: ",
                                     List => The_Unit.The_Inherited_Units);
      end if;
   end Output_Object;

   -----------------------------------------------------------------------------

   procedure Output_Id (The_Unit : in Id) is
      --# hide Output_Id;
   begin
      SparkMakeDebug.Report_Text (Text => "Unit debug:");
      SparkMakeDebug.Report_Cond_Text (Cond       => The_Unit = Null_Id,
                                       True_Text  => "Id is null",
                                       False_Text => "Id details below");
      if The_Unit /= Null_Id then
         SparkMakeDebug.Report_Text_E_Text (Text   => "Name: ",
                                            E_Text => The_Unit.The_Name);
         SparkMakeDebug.Report_Text (Text => "Kind: " & Kind'Image (The_Unit.The_Kind));
      end if;
   end Output_Id;

end Unit;
