-------------------------------------------------------------------------------
-- (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;
with SparkFormatCommandLineData;

separate (SPARKProgram)
package body Reformatter is

   Unmoded_Str           : constant String := "";
   In_Mode_Str           : constant String := "in";
   Out_Mode_Str          : constant String := "out";
   In_Out_Mode_Str       : constant String := "in out";
   Protected_Unmoded_Str : constant String := "protected";
   Protected_In_Str      : constant String := "protected in";
   Protected_Out_Str     : constant String := "protected out";
   Task_Modifier_Str     : constant String := "task";

   type Format_Info is record
      Start_Col             : E_Strings.Positions;
      Modifier_Col          : E_Strings.Positions;
      Primary_Id_Col        : E_Strings.Positions;
      Type_Mark_Col         : E_Strings.Positions;
      Properties_Col        : E_Strings.Positions;
      Modifiers_Present     : Modifier_Use;
      Type_Mark_Present     : Boolean;
      Property_List_Present : Boolean;
   end record;

   -- A simple lexer is required so that the capitalisation of
   -- names - particularly predefined type_marks such as Natural
   -- are not converted to "NATURAL".  Also simplifies parsing of
   -- property lists.
   -- The lexer assumes that the input file contains only annotations
   -- and thus the annotation start and continuation symbols can be treated
   -- as whitespace.

   --# inherit Ada.Characters.Handling,
   --#         Ada.Characters.Latin_1,
   --#         Annotations,
   --#         CommandLineData,
   --#         E_Strings,
   --#         SPARK_IO,
   --#         White_Space;
   package SimpleLex is

      type State is private;

      type Token_Type is (
                          RW_Derives,
                          RW_Global,
                          RW_Inherit,
                          RW_Initializes,
                          RW_Main_Program,
                          RW_Own,
                          RW_From,
                          RW_In,
                          RW_Is,
                          RW_Out,
                          RW_Protected,
                          RW_Task,
                          Colon,
                          Comma,
                          Point,
                          Semicolon,
                          Other_Punct, -- for simple annotations we do not need to know any others
                          Annotation_Start,
                          Annotation_End,
                          Identifier,
                          Property_List);

      subtype Annotation_Signifiers is Token_Type range RW_Derives .. RW_Own;

      type Token_Record is record
         Token       : Token_Type;
         Token_Value : E_Strings.T;
      end record;

      procedure Initialise (Input_File : in     SPARK_IO.File_Type;
                            Anno       : in     Annotations.Anno_Type;
                            Lex_State  :    out State);
      --# derives Lex_State from Anno,
      --#                        Input_File;

      procedure Next (This      : in out State;
                      Token_Rec :    out Token_Record);
      --# global in     CommandLineData.Content;
      --#        in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys,
      --#         This              from SPARK_IO.File_Sys,
      --#                                This &
      --#         Token_Rec         from CommandLineData.Content,
      --#                                SPARK_IO.File_Sys,
      --#                                This;

      function Get_Col_No (This : State) return E_Strings.Positions;

   private

      type State is record
         File          : SPARK_IO.File_Type;
         Anno          : Annotations.Anno_Type;
         Line          : E_Strings.T;
         Index         : E_Strings.Positions;
         In_Annotation : Boolean;
      end record;

   end SimpleLex;

   function "=" (Left, Right : SimpleLex.Token_Type) return Boolean renames SimpleLex."=";

   package body SimpleLex is separate;

   procedure Initialise
     (Anno         : in     Annotations.Anno_Type;
      Dotted_Names : in     Boolean;
      The_Heap     : in out Heap.HeapRecord;
      This         :    out State)
   is
      Loc_Modifier_Rel : LexTokenManager.Relation_Algebra.Relation;
      Loc_Type_Rel     : LexTokenManager.Relation_Algebra.String.Relation;
      Loc_Property_Rel : LexTokenManager.Relation_Algebra.String.Relation;
   begin
      LexTokenManager.Relation_Algebra.Create_Relation (The_Heap => The_Heap,
                                                        R        => Loc_Modifier_Rel);
      LexTokenManager.Relation_Algebra.String.Create_Relation (The_Heap => The_Heap,
                                                               R        => Loc_Type_Rel);
      LexTokenManager.Relation_Algebra.String.Create_Relation (The_Heap => The_Heap,
                                                               R        => Loc_Property_Rel);

      This :=
        State'
        (Anno               => Anno,
         Relations          => Relation_Type'(Modifier_Rel => Loc_Modifier_Rel,
                                              Type_Rel     => Loc_Type_Rel,
                                              Property_Rel => Loc_Property_Rel),
         Parse_Stats        => Statistics_Type'(Start_Col             => E_Strings.Positions'First,
                                                Max_Modifier_Length   => 0,
                                                Max_Primary_Id_Length => 0,
                                                Max_Type_Mark_Length  => 0,
                                                Modifiers_Present     => Modifier_Use'(others => False),
                                                Type_Mark_Present     => False,
                                                Property_List_Present => False),
         Allow_Dotted_Names => Dotted_Names,
         Success            => True);
   end Initialise;

   function Modifier_Length (Modifier : Variable_Modifier) return Natural is
      Result : Natural;
   begin
      case Modifier is
         when Unmoded =>
            Result := Unmoded_Str'Length;
         when In_Mode =>
            Result := In_Mode_Str'Length;
         when Out_Mode =>
            Result := Out_Mode_Str'Length;
         when In_Out_Mode =>
            Result := In_Out_Mode_Str'Length;
         when Protected_Unmoded =>
            Result := Protected_Unmoded_Str'Length;
         when Protected_In =>
            Result := Protected_In_Str'Length;
         when Protected_Out =>
            Result := Protected_Out_Str'Length;
         when Task_Modifier =>
            Result := Task_Modifier_Str'Length;
      end case;
      return Result;
   end Modifier_Length;

   function Modifier_String (Modifier : Variable_Modifier) return E_Strings.T is
      Result : E_Strings.T;
   begin
      case Modifier is
         when Unmoded =>
            Result := E_Strings.Copy_String (Str => Unmoded_Str);
         when In_Mode =>
            Result := E_Strings.Copy_String (Str => In_Mode_Str);
         when Out_Mode =>
            Result := E_Strings.Copy_String (Str => Out_Mode_Str);
         when In_Out_Mode =>
            Result := E_Strings.Copy_String (Str => In_Out_Mode_Str);
         when Protected_Unmoded =>
            Result := E_Strings.Copy_String (Str => Protected_Unmoded_Str);
         when Protected_In =>
            Result := E_Strings.Copy_String (Str => Protected_In_Str);
         when Protected_Out =>
            Result := E_Strings.Copy_String (Str => Protected_Out_Str);
         when Task_Modifier =>
            Result := E_Strings.Copy_String (Str => Task_Modifier_Str);
      end case;
      return Result;
   end Modifier_String;

   procedure Parse_Modifiers
     (Lex           : in out SimpleLex.State;
      Token         : in out SimpleLex.Token_Record;
      Modifier_Type :    out Variable_Modifier)
   --# global in     CommandLineData.Content;
   --#        in out SPARK_IO.File_Sys;
   --# derives Lex,
   --#         Modifier_Type,
   --#         SPARK_IO.File_Sys,
   --#         Token             from CommandLineData.Content,
   --#                                Lex,
   --#                                SPARK_IO.File_Sys,
   --#                                Token;
   is
   begin
      case Token.Token is
         when SimpleLex.RW_In =>
            Modifier_Type := In_Mode;
            SimpleLex.Next (This      => Lex,
                            Token_Rec => Token);
         when SimpleLex.RW_Out =>
            Modifier_Type := Out_Mode;
            SimpleLex.Next (This      => Lex,
                            Token_Rec => Token);
         when SimpleLex.RW_Protected =>
            SimpleLex.Next (This      => Lex,
                            Token_Rec => Token);
            case Token.Token is
               when SimpleLex.RW_In =>
                  Modifier_Type := Protected_In;
                  SimpleLex.Next (This      => Lex,
                                  Token_Rec => Token);
               when SimpleLex.RW_Out =>
                  Modifier_Type := Protected_Out;
                  SimpleLex.Next (This      => Lex,
                                  Token_Rec => Token);
               when others =>
                  Modifier_Type := Protected_Unmoded;
                  -- The current token is still required in this case
            end case;
         when SimpleLex.RW_Task =>
            Modifier_Type := Task_Modifier;
            SimpleLex.Next (This      => Lex,
                            Token_Rec => Token);
         when others =>
            Modifier_Type := Unmoded;
            -- The current token is still required in this case
      end case;
   end Parse_Modifiers;

   procedure Parse_Name
     (Lex                : in out SimpleLex.State;
      Allow_Dotted_Names : in     Boolean;
      Token              : in out SimpleLex.Token_Record;
      Name               :    out E_Strings.T;
      OK                 :    out Boolean)
   --# global in     CommandLineData.Content;
   --#        in out SPARK_IO.File_Sys;
   --# derives Lex,
   --#         OK,
   --#         SPARK_IO.File_Sys,
   --#         Token             from Allow_Dotted_Names,
   --#                                CommandLineData.Content,
   --#                                Lex,
   --#                                SPARK_IO.File_Sys &
   --#         Name              from Allow_Dotted_Names,
   --#                                CommandLineData.Content,
   --#                                Lex,
   --#                                SPARK_IO.File_Sys,
   --#                                Token;
   is
      Loc_Name : E_Strings.T;
      Loc_OK   : Boolean;
   begin
      Loc_OK   := True;
      Loc_Name := Token.Token_Value;
      SimpleLex.Next (This      => Lex,
                      Token_Rec => Token);
      if Allow_Dotted_Names then
         loop
            exit when (not Loc_OK) or Token.Token /= SimpleLex.Point;
            E_Strings.Append_String (E_Str => Loc_Name,
                                     Str   => ".");
            SimpleLex.Next (This      => Lex,
                            Token_Rec => Token);
            if Token.Token = SimpleLex.Identifier then
               E_Strings.Append_Examiner_String (E_Str1 => Loc_Name,
                                                 E_Str2 => Token.Token_Value);
               SimpleLex.Next (This      => Lex,
                               Token_Rec => Token);
            else
               Loc_OK := False;
            end if;
         end loop;
      end if;

      Name := Loc_Name;
      OK   := Loc_OK;
   end Parse_Name;

   procedure Parse_Name_List
     (Lex                : in out SimpleLex.State;
      Allow_Dotted_Names : in     Boolean;
      Token              : in out SimpleLex.Token_Record;
      The_Heap           : in out Heap.HeapRecord;
      The_Seq            : in     LexTokenManager.Seq_Algebra.Seq;
      Max_Name_Length    : in out E_Strings.Lengths;
      OK                 :    out Boolean)
   --# global in     CommandLineData.Content;
   --#        in out LexTokenManager.State;
   --#        in out SPARK_IO.File_Sys;
   --#        in out Statistics.TableUsage;
   --# derives Lex,
   --#         LexTokenManager.State,
   --#         Max_Name_Length,
   --#         SPARK_IO.File_Sys,
   --#         Token                 from *,
   --#                                    Allow_Dotted_Names,
   --#                                    CommandLineData.Content,
   --#                                    Lex,
   --#                                    SPARK_IO.File_Sys,
   --#                                    Token &
   --#         OK                    from Allow_Dotted_Names,
   --#                                    CommandLineData.Content,
   --#                                    Lex,
   --#                                    SPARK_IO.File_Sys,
   --#                                    Token &
   --#         Statistics.TableUsage,
   --#         The_Heap              from *,
   --#                                    Allow_Dotted_Names,
   --#                                    CommandLineData.Content,
   --#                                    Lex,
   --#                                    LexTokenManager.State,
   --#                                    SPARK_IO.File_Sys,
   --#                                    The_Heap,
   --#                                    The_Seq,
   --#                                    Token;
   is
      Loc_OK   : Boolean;
      Name     : E_Strings.T;
      Lex_Name : LexTokenManager.Lex_String;
   begin
      Loc_OK := Token.Token = SimpleLex.Identifier;

      while Loc_OK and Token.Token = SimpleLex.Identifier loop
         Parse_Name (Lex                => Lex,
                     Allow_Dotted_Names => Allow_Dotted_Names,
                     Token              => Token,
                     Name               => Name,
                     OK                 => Loc_OK);
         if Loc_OK then
            LexTokenManager.Insert_Examiner_String (Str     => Name,
                                                    Lex_Str => Lex_Name);
            LexTokenManager.Seq_Algebra.Add_Member (The_Heap    => The_Heap,
                                                    S           => The_Seq,
                                                    Given_Value => Lex_Name);
            if E_Strings.Get_Length (E_Str => Name) > Max_Name_Length then
               Max_Name_Length := E_Strings.Get_Length (E_Str => Name);
            end if;

            case Token.Token is
               when SimpleLex.Comma =>
                  SimpleLex.Next (This      => Lex,
                                  Token_Rec => Token);
               when SimpleLex.Identifier =>
                  -- Two successive identifiers are not syntactically correct
                  Loc_OK := False;
               when others =>
                  -- loop will terminate at current token
                  null;
            end case;
         end if;
      end loop;

      OK := Loc_OK;
   end Parse_Name_List;

   procedure Parse_Type_Mark
     (Lex       : in out SimpleLex.State;
      Token     : in out SimpleLex.Token_Record;
      Type_Mark :    out E_Strings.T;
      OK        : in out Boolean)
   --# global in     CommandLineData.Content;
   --#        in out SPARK_IO.File_Sys;
   --# derives Lex,
   --#         OK,
   --#         SPARK_IO.File_Sys,
   --#         Token,
   --#         Type_Mark         from CommandLineData.Content,
   --#                                Lex,
   --#                                OK,
   --#                                SPARK_IO.File_Sys,
   --#                                Token;
   is
      Type_Mark_Str : E_Strings.T;
   begin
      if OK and Token.Token = SimpleLex.Colon then
         SimpleLex.Next (This      => Lex,
                         Token_Rec => Token);

         if Token.Token = SimpleLex.Identifier then
            Parse_Name
              (Lex                => Lex,
               Allow_Dotted_Names => Allow_Dotted_Names_Const,
               Token              => Token,
               Name               => Type_Mark_Str,
               OK                 => OK);
         else
            OK            := False;
            Type_Mark_Str := E_Strings.Empty_String;
         end if;
      else
         Type_Mark_Str := E_Strings.Empty_String;
      end if;

      Type_Mark := Type_Mark_Str;
   end Parse_Type_Mark;

   procedure Parse_Properties
     (Lex           : in out SimpleLex.State;
      Token         : in out SimpleLex.Token_Record;
      Property_List :    out E_Strings.T;
      OK            : in     Boolean)
   --# global in     CommandLineData.Content;
   --#        in out SPARK_IO.File_Sys;
   --# derives Lex,
   --#         SPARK_IO.File_Sys from Lex,
   --#                                OK,
   --#                                SPARK_IO.File_Sys,
   --#                                Token &
   --#         Property_List     from OK,
   --#                                Token &
   --#         Token             from *,
   --#                                CommandLineData.Content,
   --#                                Lex,
   --#                                OK,
   --#                                SPARK_IO.File_Sys;
   is
   begin
      if OK and Token.Token = SimpleLex.Property_List then
         Property_List := Token.Token_Value;
         SimpleLex.Next (This      => Lex,
                         Token_Rec => Token);
      else
         Property_List := E_Strings.Empty_String;
      end if;
   end Parse_Properties;

   procedure Parse_The_Annotation
     (Lex                : in     SimpleLex.State;
      Token              : in     SimpleLex.Token_Record;
      Allow_Dotted_Names : in     Boolean;
      The_Heap           : in out Heap.HeapRecord;
      Relations          : in     Relation_Type;
      Parse_Stats        : in out Statistics_Type;
      OK                 :    out Boolean)
   --# global in     CommandLineData.Content;
   --#        in out LexTokenManager.State;
   --#        in out SPARK_IO.File_Sys;
   --#        in out Statistics.TableUsage;
   --# derives LexTokenManager.State,
   --#         SPARK_IO.File_Sys     from *,
   --#                                    Allow_Dotted_Names,
   --#                                    CommandLineData.Content,
   --#                                    Lex,
   --#                                    SPARK_IO.File_Sys,
   --#                                    Token &
   --#         OK                    from Allow_Dotted_Names,
   --#                                    CommandLineData.Content,
   --#                                    Lex,
   --#                                    SPARK_IO.File_Sys,
   --#                                    Token &
   --#         Parse_Stats,
   --#         Statistics.TableUsage,
   --#         The_Heap              from *,
   --#                                    Allow_Dotted_Names,
   --#                                    CommandLineData.Content,
   --#                                    Lex,
   --#                                    LexTokenManager.State,
   --#                                    Relations,
   --#                                    SPARK_IO.File_Sys,
   --#                                    The_Heap,
   --#                                    Token;
   is
      Loc_OK            : Boolean;
      Loc_Lex           : SimpleLex.State;
      Next_Token        : SimpleLex.Token_Record;
      Name_List         : LexTokenManager.Seq_Algebra.Seq;
      First_Name        : LexTokenManager.Seq_Algebra.Member_Of_Seq;
      Type_Mark         : E_Strings.T;
      Property_List     : E_Strings.T;
      First_Name_Lex    : LexTokenManager.Lex_String;
      Type_Mark_Lex     : LexTokenManager.Lex_String;
      Property_List_Lex : LexTokenManager.Lex_String;
      Modifier          : Variable_Modifier;
   begin -- Parse_The_Annotation
      Loc_OK     := True;
      Next_Token := Token;
      Loc_Lex    := Lex;

      while Loc_OK
        and then not (Next_Token.Token in SimpleLex.Annotation_Signifiers or else Next_Token.Token = SimpleLex.Annotation_End) loop

         LexTokenManager.Seq_Algebra.Create_Seq (The_Heap => The_Heap,
                                                 S        => Name_List);

         Parse_Modifiers (Lex           => Loc_Lex,
                          Token         => Next_Token,
                          Modifier_Type => Modifier);
         Parse_Name_List
           (Lex                => Loc_Lex,
            Allow_Dotted_Names => Allow_Dotted_Names,
            Token              => Next_Token,
            The_Heap           => The_Heap,
            The_Seq            => Name_List,
            Max_Name_Length    => Parse_Stats.Max_Primary_Id_Length,
            OK                 => Loc_OK);
         Parse_Type_Mark (Lex       => Loc_Lex,
                          Token     => Next_Token,
                          Type_Mark => Type_Mark,
                          OK        => Loc_OK);
         Parse_Properties (Lex           => Loc_Lex,
                           Token         => Next_Token,
                           Property_List => Property_List,
                           OK            => Loc_OK);

         Loc_OK := Loc_OK and Next_Token.Token = SimpleLex.Semicolon;
         if Loc_OK then

            if not E_Strings.Is_Empty (E_Str => Type_Mark) then
               -- A type_mark applies to all names in the list
               LexTokenManager.Insert_Examiner_String (Str     => Type_Mark,
                                                       Lex_Str => Type_Mark_Lex);
               LexTokenManager.Relation_Algebra.String.Add_Col
                 (The_Heap => The_Heap,
                  R        => Relations.Type_Rel,
                  J        => Type_Mark_Lex,
                  S        => Name_List);
               Parse_Stats.Type_Mark_Present := True;
               if E_Strings.Get_Length (E_Str => Type_Mark) > Parse_Stats.Max_Type_Mark_Length then
                  Parse_Stats.Max_Type_Mark_Length := E_Strings.Get_Length (E_Str => Type_Mark);
               end if;
            end if;

            if not E_Strings.Is_Empty (E_Str => Property_List) then
               -- A property_list applies to all names in the list
               LexTokenManager.Insert_Examiner_String (Str     => Property_List,
                                                       Lex_Str => Property_List_Lex);
               LexTokenManager.Relation_Algebra.String.Add_Col
                 (The_Heap => The_Heap,
                  R        => Relations.Property_Rel,
                  J        => Property_List_Lex,
                  S        => Name_List);
               Parse_Stats.Property_List_Present := True;
            end if;

            Parse_Stats.Modifiers_Present (Modifier) := True;
            if Modifier_Length (Modifier => Modifier) > Parse_Stats.Max_Modifier_Length then
               Parse_Stats.Max_Modifier_Length := Modifier_Length (Modifier => Modifier);
            end if;
            if Modifier /= Unmoded then
               -- A modifier only applies to the first name in the list
               First_Name     := LexTokenManager.Seq_Algebra.First_Member (The_Heap => The_Heap,
                                                                           S        => Name_List);
               First_Name_Lex := LexTokenManager.Seq_Algebra.Value_Of_Member (The_Heap => The_Heap,
                                                                              M        => First_Name);
               LexTokenManager.Seq_Algebra.Remove_Member (The_Heap    => The_Heap,
                                                          S           => Name_List,
                                                          Given_Value => First_Name_Lex);
               LexTokenManager.Relation_Algebra.Insert_Pair
                 (The_Heap => The_Heap,
                  R        => Relations.Modifier_Rel,
                  I        => Variable_Modifier'Pos (Modifier),
                  J        => First_Name_Lex);
            end if;
            -- The remaining names in the list are unmoded
            if not LexTokenManager.Seq_Algebra.Is_Empty_Seq (The_Heap => The_Heap,
                                                             S        => Name_List) then
               LexTokenManager.Relation_Algebra.Add_Row
                 (The_Heap => The_Heap,
                  R        => Relations.Modifier_Rel,
                  I        => Variable_Modifier'Pos (Unmoded),
                  S        => Name_List);
               Parse_Stats.Modifiers_Present (Unmoded) := True;
            end if;

            SimpleLex.Next (This      => Loc_Lex,
                            Token_Rec => Next_Token);
         end if;

         LexTokenManager.Seq_Algebra.Dispose_Of_Seq (The_Heap => The_Heap,
                                                     S        => Name_List);

      end loop;

      OK := Loc_OK;

   end Parse_The_Annotation;

   procedure Parse (This           : in out State;
                    The_Heap       : in out Heap.HeapRecord;
                    Temporary_File : in out SPARK_IO.File_Type) is
      Loc_OK        : Boolean;
      Loc_Start_Col : E_Strings.Positions;
      Token         : SimpleLex.Token_Record;
      Lex           : SimpleLex.State;
      Parse_Stats   : Statistics_Type;
   begin
      Parse_Stats := This.Parse_Stats;
      File_IO.Reset (Temporary_File);
      SimpleLex.Initialise (Input_File => Temporary_File,
                            Anno       => This.Anno,
                            Lex_State  => Lex);
      SimpleLex.Next (This      => Lex,
                      Token_Rec => Token);
      if Token.Token = SimpleLex.Annotation_Start then
         -- The start of an annotation has been located
         -- Take account of the length of the annotation_start symbol
         Loc_Start_Col := SimpleLex.Get_Col_No (This => Lex);
         if Loc_Start_Col >= 3 then
            Loc_Start_Col := Loc_Start_Col - 3;
         else
            Loc_Start_Col := 1;
         end if;

         SimpleLex.Next (This      => Lex,
                         Token_Rec => Token);

         if E_Strings.Eq_String (E_Str1 => Token.Token_Value,
                                 E_Str2 => Annotations.Intro (This.Anno)) then
            SimpleLex.Next (This      => Lex,
                            Token_Rec => Token);
            Parse_The_Annotation
              (Lex                => Lex,
               Token              => Token,
               Allow_Dotted_Names => This.Allow_Dotted_Names,
               The_Heap           => The_Heap,
               Relations          => This.Relations,
               Parse_Stats        => Parse_Stats,
               OK                 => Loc_OK);
         else
            Loc_OK := False;
         end if;
      else
         Loc_OK        := False;
         Loc_Start_Col := 1;  -- Assume a Start_Col of 0 if not start of anno
      end if;

      This.Success          := Loc_OK;
      Parse_Stats.Start_Col := Loc_Start_Col;
      This.Parse_Stats      := Parse_Stats;

   end Parse;

   procedure Get_Related
     (The_Heap    : in out Heap.HeapRecord;
      Relation    : in     LexTokenManager.Relation_Algebra.String.Relation;
      Var_Name    : in     LexTokenManager.Seq_Algebra.Member_Of_Seq;
      Related_Str :    out E_Strings.T)
   --# global in     LexTokenManager.State;
   --#        in out Statistics.TableUsage;
   --# derives Related_Str,
   --#         The_Heap              from LexTokenManager.State,
   --#                                    Relation,
   --#                                    The_Heap,
   --#                                    Var_Name &
   --#         Statistics.TableUsage from *,
   --#                                    LexTokenManager.State,
   --#                                    Relation,
   --#                                    The_Heap,
   --#                                    Var_Name;
   is
      Seq          : LexTokenManager.Seq_Algebra.Seq;
      First_Member : LexTokenManager.Seq_Algebra.Member_Of_Seq;
      Result       : E_Strings.T;
   begin
      LexTokenManager.Relation_Algebra.String.Row_Extraction
        (The_Heap    => The_Heap,
         R           => Relation,
         Given_Index => LexTokenManager.Seq_Algebra.Value_Of_Member (The_Heap => The_Heap,
                                                                     M        => Var_Name),
         S           => Seq);

      -- There should be only zero or one related string with a variable
      First_Member := LexTokenManager.Seq_Algebra.First_Member (The_Heap => The_Heap,
                                                                S        => Seq);
      if LexTokenManager.Seq_Algebra.Is_Null_Member (M => First_Member) then
         Result := E_Strings.Empty_String;
      else
         Result :=
           LexTokenManager.Lex_String_To_String
           (Lex_Str => LexTokenManager.Seq_Algebra.Value_Of_Member (The_Heap => The_Heap,
                                                                    M        => First_Member));
      end if;

      LexTokenManager.Seq_Algebra.Dispose_Of_Seq (The_Heap => The_Heap,
                                                  S        => Seq);

      Related_Str := Result;
   end Get_Related;

   procedure Write_Property_List
     (Anno       : in Annotations.Anno_Type;
      Output     : in SPARK_IO.File_Type;
      Formatting : in Format_Info;
      Properties : in E_Strings.T)
   --# global in     CommandLineData.Content;
   --#        in     SparkFormatCommandLineData.Content;
   --#        in out SPARK_IO.File_Sys;
   --# derives SPARK_IO.File_Sys from *,
   --#                                Anno,
   --#                                CommandLineData.Content,
   --#                                Formatting,
   --#                                Output,
   --#                                Properties,
   --#                                SparkFormatCommandLineData.Content;
   is
      I : E_Strings.Positions;
   begin
      I := 1;
      if SparkFormatCommandLineData.Content.Properties_Indent /= SparkFormatCommandLineData.Inline then

         SPARK_IO.New_Line (Output, 1);
         Annotations.Write (Anno, Output, Formatting.Start_Col);
      end if;
      SPARK_IO.Set_Col (Output, Formatting.Properties_Col);
      while I <= E_Strings.Get_Length (E_Str => Properties) loop
         if I + 2 <= E_Strings.Get_Length (E_Str => Properties)
           and then E_Strings.Get_Element (E_Str => Properties,
                                           Pos   => I) = '-'
           and then E_Strings.Get_Element (E_Str => Properties,
                                           Pos   => I + 1) = '-'
           and then E_Strings.Get_Element (E_Str => Properties,
                                           Pos   => I + 2) = CommandLineData.Content.Anno_Char then
            SPARK_IO.New_Line (Output, 1);
            Annotations.Write (Anno, Output, Formatting.Start_Col);
            SPARK_IO.Set_Col (Output, Formatting.Properties_Col);
            I := I + 3;
         else
            SPARK_IO.Put_Char (Output, E_Strings.Get_Element (E_Str => Properties,
                                                              Pos   => I));
            I := I + 1;
         end if;
      end loop;
   end Write_Property_List;

   procedure Write_Name
     (Anno          : in     Annotations.Anno_Type;
      Output        : in     SPARK_IO.File_Type;
      Formatting    : in     Format_Info;
      First_Name    : in out Boolean;
      May_Use_Comma : in out Boolean;
      The_Heap      : in out Heap.HeapRecord;
      Name          : in     LexTokenManager.Seq_Algebra.Member_Of_Seq;
      Modifier      : in     Variable_Modifier;
      Relations     : in     Relation_Type)
   --# global in     CommandLineData.Content;
   --#        in     LexTokenManager.State;
   --#        in     SparkFormatCommandLineData.Content;
   --#        in out SPARK_IO.File_Sys;
   --#        in out Statistics.TableUsage;
   --# derives First_Name            from * &
   --#         May_Use_Comma         from Formatting,
   --#                                    LexTokenManager.State,
   --#                                    Modifier,
   --#                                    Name,
   --#                                    Relations,
   --#                                    The_Heap &
   --#         SPARK_IO.File_Sys     from *,
   --#                                    Anno,
   --#                                    CommandLineData.Content,
   --#                                    First_Name,
   --#                                    Formatting,
   --#                                    LexTokenManager.State,
   --#                                    May_Use_Comma,
   --#                                    Modifier,
   --#                                    Name,
   --#                                    Output,
   --#                                    Relations,
   --#                                    SparkFormatCommandLineData.Content,
   --#                                    The_Heap &
   --#         Statistics.TableUsage,
   --#         The_Heap              from *,
   --#                                    Formatting,
   --#                                    LexTokenManager.State,
   --#                                    Name,
   --#                                    Relations,
   --#                                    The_Heap;
   is
      Type_Mark        : E_Strings.T;
      Property_List    : E_Strings.T;
      Simple_Name_List : Boolean;
   begin
      if Formatting.Type_Mark_Present then
         Get_Related (The_Heap    => The_Heap,
                      Relation    => Relations.Type_Rel,
                      Var_Name    => Name,
                      Related_Str => Type_Mark);
      else
         Type_Mark := E_Strings.Empty_String;
      end if;

      if Formatting.Property_List_Present then
         Get_Related (The_Heap    => The_Heap,
                      Relation    => Relations.Property_Rel,
                      Var_Name    => Name,
                      Related_Str => Property_List);
      else
         Property_List := E_Strings.Empty_String;
      end if;

      Simple_Name_List := Modifier = Unmoded
        and then E_Strings.Is_Empty (E_Str => Type_Mark)
        and then E_Strings.Is_Empty (E_Str => Property_List);

      if First_Name then
         First_Name := False;
         if Annotations.Indent (Anno) /= SparkFormatCommandLineData.Inline then
            SPARK_IO.New_Line (Output, 1);
            Annotations.Write (Anno, Output, Formatting.Start_Col);
         end if;
      else
         if May_Use_Comma and Simple_Name_List then
            SPARK_IO.Put_Line (Output, ",", 0);
         else
            SPARK_IO.Put_Line (Output, ";", 0);
         end if;
         Annotations.Write (Anno, Output, Formatting.Start_Col);
      end if;
      --# assert True;
      if Modifier /= Unmoded then
         SPARK_IO.Set_Col (Output, Formatting.Modifier_Col);
         E_Strings.Put_String (File  => Output,
                               E_Str => Modifier_String (Modifier => Modifier));
      end if;

      SPARK_IO.Set_Col (Output, Formatting.Primary_Id_Col);
      E_Strings.Put_String
        (File  => Output,
         E_Str => LexTokenManager.Lex_String_To_String
           (Lex_Str => LexTokenManager.Seq_Algebra.Value_Of_Member (The_Heap => The_Heap,
                                                                    M        => Name)));
      if not E_Strings.Is_Empty (E_Str => Type_Mark) then
         SPARK_IO.Set_Col (Output, Formatting.Type_Mark_Col);
         SPARK_IO.Put_String (Output, ": ", 0);
         E_Strings.Put_String (File  => Output,
                               E_Str => Type_Mark);
      end if;

      if not E_Strings.Is_Empty (E_Str => Property_List) then
         Write_Property_List (Anno       => Anno,
                              Output     => Output,
                              Formatting => Formatting,
                              Properties => Property_List);
      end if;

      May_Use_Comma := Simple_Name_List;

   end Write_Name;

   procedure Write_Name_List
     (Anno       : in     Annotations.Anno_Type;
      Output     : in     SPARK_IO.File_Type;
      Formatting : in     Format_Info;
      The_Heap   : in out Heap.HeapRecord;
      Relations  : in     Relation_Type)
   --# global in     CommandLineData.Content;
   --#        in     LexTokenManager.State;
   --#        in     SparkFormatCommandLineData.Content;
   --#        in out SPARK_IO.File_Sys;
   --#        in out Statistics.TableUsage;
   --# derives SPARK_IO.File_Sys     from *,
   --#                                    Anno,
   --#                                    CommandLineData.Content,
   --#                                    Formatting,
   --#                                    LexTokenManager.State,
   --#                                    Output,
   --#                                    Relations,
   --#                                    SparkFormatCommandLineData.Content,
   --#                                    The_Heap &
   --#         Statistics.TableUsage,
   --#         The_Heap              from *,
   --#                                    Formatting,
   --#                                    LexTokenManager.State,
   --#                                    Relations,
   --#                                    The_Heap;
   is
      The_Seq       : LexTokenManager.Seq_Algebra.Seq;
      First_Name    : Boolean;
      May_Use_Comma : Boolean;
      The_Iterator  : Iteration.Iterator;
   begin
      First_Name    := True;
      May_Use_Comma := False;

      for Modifier in Variable_Modifier loop

         if Formatting.Modifiers_Present (Modifier) then
            -- extract by modifier
            LexTokenManager.Relation_Algebra.Row_Extraction
              (The_Heap    => The_Heap,
               R           => Relations.Modifier_Rel,
               Given_Index => Variable_Modifier'Pos (Modifier),
               S           => The_Seq);

            Iteration.Initialise (The_Heap     => The_Heap,
                                  The_Seq      => The_Seq,
                                  The_Iterator => The_Iterator);

            while not Iteration.Complete (The_Iterator) loop

               Write_Name
                 (Anno          => Anno,
                  Output        => Output,
                  Formatting    => Formatting,
                  First_Name    => First_Name,
                  May_Use_Comma => May_Use_Comma,
                  The_Heap      => The_Heap,
                  Name          => Iteration.Current_Member (The_Iterator),
                  Modifier      => Modifier,
                  Relations     => Relations);

               Iteration.Next (The_Heap, The_Iterator);

            end loop;

            LexTokenManager.Seq_Algebra.Dispose_Of_Seq (The_Heap => The_Heap,
                                                        S        => The_Seq);
         end if;

      end loop;

   end Write_Name_List;

   procedure Reformat
     (This           : in     State;
      The_Heap       : in out Heap.HeapRecord;
      Temporary_File : in out SPARK_IO.File_Type;
      Output         : in     SPARK_IO.File_Type;
      Success        :    out Boolean)
   is
      Formatting     : Format_Info;
      Modifier_Col   : E_Strings.Positions;
      Primary_Id_Col : E_Strings.Positions;
      Type_Mark_Col  : E_Strings.Positions;
      Properties_Col : E_Strings.Positions;
   begin
      Modifier_Col := Annotations.Name1_Start_Col (This      => This.Anno,
                                                   Start_Col => This.Parse_Stats.Start_Col);

      if This.Parse_Stats.Max_Modifier_Length > 0 then
         Primary_Id_Col := (Modifier_Col + This.Parse_Stats.Max_Modifier_Length) + 1;
      else
         Primary_Id_Col := Modifier_Col;
      end if;

      Type_Mark_Col := (Primary_Id_Col + This.Parse_Stats.Max_Primary_Id_Length) + 1;

      if SparkFormatCommandLineData.Content.Properties_Indent /= SparkFormatCommandLineData.Inline then

         Properties_Col := SparkFormatCommandLineData.Content.Properties_Indent + 4;
      else
         -- Extra 2 columns are for the ": " preceding the Type_Mark
         Properties_Col := (Type_Mark_Col + This.Parse_Stats.Max_Type_Mark_Length) + 3;
      end if;

      Formatting :=
        Format_Info'
        (Start_Col             => This.Parse_Stats.Start_Col,
         Modifier_Col          => Modifier_Col,
         Primary_Id_Col        => Primary_Id_Col,
         Type_Mark_Col         => Type_Mark_Col,
         Properties_Col        => Properties_Col,
         Modifiers_Present     => This.Parse_Stats.Modifiers_Present,
         Type_Mark_Present     => This.Parse_Stats.Type_Mark_Present,
         Property_List_Present => This.Parse_Stats.Property_List_Present);

      if This.Success then
         Annotations.Write_Intro (This      => This.Anno,
                                  Output    => Output,
                                  Start_Col => Formatting.Start_Col);

         -- Writes inherits and own variables but not globals and derives
         Write_Name_List
           (Anno       => This.Anno,
            Output     => Output,
            Formatting => Formatting,
            The_Heap   => The_Heap,
            Relations  => This.Relations);

         SPARK_IO.Put_Line (Output, ";", 0);

      else
         File_IO.Reset (Temporary_File);
         SPARKProgram.Copy (Temporary_File, Output);
      end if;

      Success := This.Success;

   end Reformat;

   procedure Finalise (This     : in out State;
                       The_Heap : in out Heap.HeapRecord) is
      Loc_Relations : Relation_Type;
   begin
      Loc_Relations := This.Relations;
      LexTokenManager.Relation_Algebra.Dispose_Of_Relation (The_Heap => The_Heap,
                                                            R        => This.Relations.Modifier_Rel);
      LexTokenManager.Relation_Algebra.String.Dispose_Of_Relation (The_Heap => The_Heap,
                                                                   R        => This.Relations.Type_Rel);
      LexTokenManager.Relation_Algebra.String.Dispose_Of_Relation (The_Heap => The_Heap,
                                                                   R        => This.Relations.Property_Rel);
      This.Relations := Loc_Relations;
   end Finalise;

end Reformatter;
