-------------------------------------------------------------------------------
-- (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 LexTokenManager;
with SparkLex;
with SP_Symbols;
with SparkMakeErrors;

use type SP_Symbols.SP_Terminal;

package body TokenManager is

   procedure Next (It : in out Iterator) is
      Unused     : Boolean;
      Token_Kind : SP_Symbols.SP_Terminal;
      Lex_Value  : LexTokenManager.Lex_Value;

      procedure Get_Identifier (It : in out Iterator)
      --# 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;
      --# derives ErrorHandler.Error_Context,
      --#         It,
      --#         LexTokenManager.State,
      --#         SparkLex.Curr_Line,
      --#         SPARK_IO.File_Sys          from CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         It,
      --#                                         LexTokenManager.State,
      --#                                         SparkLex.Curr_Line,
      --#                                         SPARK_IO.File_Sys;
      --# pre SparkLex.Curr_Line_Invariant (SparkLex.Curr_Line);
      --# post SparkLex.Curr_Line_Invariant (SparkLex.Curr_Line);
      is
         Unused     : Boolean;
         Token_Kind : SP_Symbols.SP_Terminal;
         Lex_Value  : LexTokenManager.Lex_Value;
      begin
         while It /= Null_Iterator loop
            --# assert SparkLex.Curr_Line_Invariant (SparkLex.Curr_Line);
            -- is there more dotted notation?
            --# accept Flow, 10, Unused, "Ineffective assignment OK";
            SparkLex.Examiner_Lex (Prog_Text   => It.File,
                                   Token       => Token_Kind,
                                   Lex_Val     => Lex_Value,
                                   Punct_Token => Unused);
            --# end accept;

            -- No, this is the end of the identifier and we have read a token too far.
            if Token_Kind /= SP_Symbols.point then
               It.Next_Token :=
                 Token'(Kind  => Token_Kind,
                        Value => LexTokenManager.Lex_String_To_String (Lex_Str => Lex_Value.Token_Str));
               exit;
            end if;

            -- Read the next part of the dotted identifier
            --# accept Flow, 10, Unused, "Ineffective assignment OK";
            SparkLex.Examiner_Lex (Prog_Text   => It.File,
                                   Token       => Token_Kind,
                                   Lex_Val     => Lex_Value,
                                   Punct_Token => Unused);
            --# end accept;

            if Token_Kind /= SP_Symbols.identifier then
               -- Invlaid syntax return a null iterator
               It := Null_Iterator;
            else
               E_Strings.Append_String (E_Str => It.Current_Token.Value,
                                        Str   => ".");
               E_Strings.Append_Examiner_String
                 (E_Str1 => It.Current_Token.Value,
                  E_Str2 => LexTokenManager.Lex_String_To_String (Lex_Str => Lex_Value.Token_Str));
            end if;
         end loop;
         --# accept Flow, 33, Unused, "Unused not references OK";
      end Get_Identifier;

   begin
      if It = Null_Iterator then
         SparkMakeErrors.Fatal ("calling TokenManager.Next with a null iterator.");

      elsif It.Is_Look_Ahead then
         It.Is_Look_Ahead := False;

      elsif It.Next_Token /= Null_Token then
         It.Current_Token := It.Next_Token;
         It.Next_Token    := Null_Token;

      else
         --# accept Flow, 10, Unused, "Ineffective assignment OK";
         SparkLex.Examiner_Lex (Prog_Text   => It.File,
                                Token       => Token_Kind,
                                Lex_Val     => Lex_Value,
                                Punct_Token => Unused);
         --# end accept;

         if Token_Kind = SP_Symbols.SPEND then
            It := Null_Iterator;
         else
            It.Current_Token :=
              Token'(Kind  => Token_Kind,
                     Value => LexTokenManager.Lex_String_To_String (Lex_Str => Lex_Value.Token_Str));
            if Token_Kind = SP_Symbols.identifier then
               Get_Identifier (It => It);
            end if;
         end if;
      end if;
      --# accept Flow, 33, Unused, "Unused not references OK";
   end Next;

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

   procedure Look_Ahead (It : in out Iterator) is
   begin
      if It = Null_Iterator then
         SparkMakeErrors.Fatal ("calling TokenManager.Look_Ahead with a null iterator.");
      elsif not It.Is_Look_Ahead then
         Next (It => It);
         It.Is_Look_Ahead := True;
      end if;
   end Look_Ahead;

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

   procedure Get_First_Token (File_Id : in     SPARK_IO.File_Type;
                              It      :    out Iterator) is
      New_It : Iterator;
   begin
      SparkLex.Clear_Line_Context;
      New_It := Iterator'(File          => File_Id,
                          Current_Token => Null_Token,
                          Next_Token    => Null_Token,
                          Is_Look_Ahead => False);
      Next (It => New_It);
      It := New_It;
   end Get_First_Token;

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

   function Is_Null (It : Iterator) return Boolean is
   begin
      return It = Null_Iterator;
   end Is_Null;

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

   function Current (It : Iterator) return Token is
      Result : Token;
   begin
      if It = Null_Iterator then
         Result := Null_Token;
      else
         Result := It.Current_Token;
      end if;
      return Result;
   end Current;

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

   function To_String (Tok : Token) return E_Strings.T is
      --# hide To_String;
      Result : E_Strings.T := E_Strings.Empty_String;
   begin
      E_Strings.Append_String (E_Str => Result,
                               Str   => SP_Symbols.SP_Terminal'Image (Tok.Kind) & ": ");
      E_Strings.Append_Examiner_String (E_Str1 => Result,
                                        E_Str2 => Tok.Value);
      return Result;
   end To_String;

end TokenManager;
