-------------------------------------------------------------------------------
-- (C) Altran Praxis Limited
-------------------------------------------------------------------------------
--
-- The SPARK toolset is free software; you can redistribute it and/or modify it
-- under terms of the GNU General Public License as published by the Free
-- Software Foundation; either version 3, or (at your option) any later
-- version. The SPARK toolset is distributed in the hope that it will be
-- useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-- Public License for more details. You should have received a copy of the GNU
-- General Public License distributed with the SPARK toolset; see file
-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy of
-- the license.
--
--=============================================================================

separate (SparkLex.Lex)
procedure GetIdent (Curr_Line : in out Line_Context;
                    Token     :    out SP_Symbols.SP_Terminal)
-- Given that the current position in the line buffer, CurrLine, indexes a
-- letter, GetIdent forward scans the input line buffer to locate the end of
-- the identifier.
-- The extent of the identifier is determined by the
-- maximal length substring starting at the current position accepted by one
-- of the following two regular expressions :-
--    identifier ::= letter {[underline] letter_or_digit}
--    illegal_id ::= identifier non_id_separator {non_id_separator}
-- where
--    non_id_separator is any character which cannot legally separate an
--    identifier from the next token in the Ada Lexis.
-- The expression which scans the longest substring is the one that is chosen.
-- The value of Token is set according to which expression was used.
-- GetIdent selects the correct expression without backtracking.
is
   type Id_State is (Underline_Letter_Or_Digit, Letter_Or_Digit_Only, Illegal_State);
   State : Id_State;
   Ch    : Character;

   function Id_Separator (Ch : Character) return Boolean is
   begin
      return Ch = ' ' or else Ch = End_Of_Text or else Format_Effector (Ch => Ch) or else Simple_Delimiter (Ch => Ch);
   end Id_Separator;

begin
   LineManager.Accept_Char (Curr_Line => Curr_Line); -- The first letter has already been parsed
   State := Underline_Letter_Or_Digit;

   loop
      --# assert E_Strings.Get_Length (Curr_Line.Conts) = E_Strings.Get_Length (Curr_Line~.Conts) and
      --#   Curr_Line.Curr_Pos > Curr_Line~.Curr_Pos and
      --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
      --#   Curr_Line.Lookahead_Pos = Curr_Line.Curr_Pos and
      --#   Curr_Line.Last_Token_Pos = Curr_Line~.Last_Token_Pos and
      --#   State in Id_State;
      Ch := E_Strings.Get_Element (E_Str => Curr_Line.Conts,
                                   Pos   => Curr_Line.Curr_Pos);
      exit when Id_Separator (Ch => Ch);
      LineManager.Accept_Char (Curr_Line => Curr_Line);
      case State is
         when Underline_Letter_Or_Digit =>
            -- Modified following to eliminate reference to tilde and
            -- also to disallow reference to double underbar in proof context
            -- this second reverses an undocumented change in the tool
            if Letter_Or_Digit (Ch => Ch) then
               null;
            elsif Ch = '_' then
               State := Letter_Or_Digit_Only;
            else
               State := Illegal_State;
            end if;
         when Letter_Or_Digit_Only =>
            if Letter_Or_Digit (Ch => Ch) then
               State := Underline_Letter_Or_Digit;
            else
               State := Illegal_State;
            end if;
         when Illegal_State =>
            null;
      end case;
   end loop;

   case State is
      when Underline_Letter_Or_Digit =>
         Token := SP_Symbols.identifier;
      when Letter_Or_Digit_Only =>
         Token := SP_Symbols.illegal_id;
      when Illegal_State =>
         Token := SP_Symbols.illegal_id;
   end case;
end GetIdent;
