-------------------------------------------------------------------------------
-- (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 Ada.Characters.Handling;
with Ada.Characters.Latin_1;
with CommandLineData;
with ErrorHandler;
with SystemErrors;

use type CommandLineData.Language_Profiles;

package body SparkLex is

   Curr_Line : Line_Context;

   End_Of_Text : constant Character := Ada.Characters.Latin_1.ETX;

   --# inherit Ada.Characters.Latin_1,
   --#         CommandLineData,
   --#         Dictionary,
   --#         ErrorHandler,
   --#         E_Strings,
   --#         LexTokenManager,
   --#         SparkLex,
   --#         SPARK_IO,
   --#         SystemErrors;
   package LineManager is

      procedure Clear_Line (Curr_Line : out SparkLex.Line_Context);
      --# derives Curr_Line from ;
      --# post E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
      --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
      --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
      --#   Curr_Line.Last_Token_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1;

      procedure Copy_In_Line (Line      : in     SparkLex.Line_Context;
                              Curr_Line :    out SparkLex.Line_Context);
      --# derives Curr_Line from Line;
      --# post Line = Curr_Line;

      procedure Record_Curr_Pos (Curr_Line : in out SparkLex.Line_Context);
      --# derives Curr_Line from *;
      --# post Curr_Line.Last_Token_Pos = Curr_Line.Curr_Pos and
      --#   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.Lookahead_Pos = Curr_Line~.Lookahead_Pos;

      procedure Reset_Curr_Pos (Curr_Line : in out SparkLex.Line_Context);
      --# derives Curr_Line from *;
      --# post Curr_Line.Curr_Pos = Curr_Line.Last_Token_Pos and
      --#   Curr_Line.Lookahead_Pos = Curr_Line.Last_Token_Pos and
      --#   E_Strings.Get_Length (Curr_Line.Conts) = E_Strings.Get_Length (Curr_Line~.Conts) and
      --#   Curr_Line.Last_Token_Pos = Curr_Line~.Last_Token_Pos;

      procedure Accept_Char (Curr_Line : in out SparkLex.Line_Context);
      --# derives Curr_Line from *;
      --# pre E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
      --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
      --# post Curr_Line.Lookahead_Pos = Curr_Line.Curr_Pos and
      --#   Curr_Line.Curr_Pos >= Curr_Line~.Curr_Pos and
      --#   (Curr_Line~.Curr_Pos <= E_Strings.Get_Length (Curr_Line~.Conts) ->
      --#      (Curr_Line.Curr_Pos > Curr_Line~.Curr_Pos)) and
      --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
      --#   E_Strings.Get_Length (Curr_Line.Conts) = E_Strings.Get_Length (Curr_Line~.Conts) and
      --#   Curr_Line.Last_Token_Pos = Curr_Line~.Last_Token_Pos;

      procedure Lookahead_Char (Curr_Line : in out SparkLex.Line_Context;
                                Ch        :    out Character);
      --# derives Ch,
      --#         Curr_Line from Curr_Line;
      --# pre E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
      --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
      --# post Curr_Line.Lookahead_Pos >= Curr_Line~.Lookahead_Pos and
      --#   (Curr_Line~.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line~.Conts) ->
      --#      (Curr_Line.Lookahead_Pos > Curr_Line~.Lookahead_Pos)) and
      --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
      --#   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.Last_Token_Pos = Curr_Line~.Last_Token_Pos;

      procedure Accept_Lookahead (Curr_Line : in out SparkLex.Line_Context);
      --# derives Curr_Line from *;
      --# pre E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
      --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
      --# post Curr_Line.Curr_Pos = Curr_Line.Lookahead_Pos and
      --#   Curr_Line.Lookahead_Pos >= Curr_Line~.Lookahead_Pos and
      --#   (Curr_Line~.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line~.Conts) ->
      --#      (Curr_Line.Lookahead_Pos > Curr_Line~.Lookahead_Pos)) and
      --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
      --#   E_Strings.Get_Length (Curr_Line.Conts) = E_Strings.Get_Length (Curr_Line~.Conts) and
      --#   Curr_Line.Last_Token_Pos = Curr_Line~.Last_Token_Pos;

      procedure Reject_Lookahead (Curr_Line : in out SparkLex.Line_Context);
      --# derives Curr_Line from *;
      --# post Curr_Line.Lookahead_Pos = Curr_Line.Curr_Pos and
      --#   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.Last_Token_Pos = Curr_Line~.Last_Token_Pos;

      function Separator (Ch : Character) return Boolean;

      procedure Next_Sig_Char (Prog_Text : in     SPARK_IO.File_Type;
                               Curr_Line : in out SparkLex.Line_Context);
      --# global in     CommandLineData.Content;
      --#        in     Dictionary.Dict;
      --#        in out ErrorHandler.Error_Context;
      --#        in out LexTokenManager.State;
      --#        in out SPARK_IO.File_Sys;
      --# derives Curr_Line,
      --#         ErrorHandler.Error_Context,
      --#         LexTokenManager.State,
      --#         SPARK_IO.File_Sys          from CommandLineData.Content,
      --#                                         Curr_Line,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         Prog_Text,
      --#                                         SPARK_IO.File_Sys;
      --# pre E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
      --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
      --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
      --#   Curr_Line.Last_Token_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
      --# post E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
      --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) and
      --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) and
      --#   Curr_Line.Last_Token_Pos <= E_Strings.Get_Length (Curr_Line.Conts) and
      --#   Curr_Line.Last_Token_Pos = Curr_Line.Curr_Pos and Curr_Line.Lookahead_Pos = Curr_Line.Curr_Pos;

      procedure Set_Context (Curr_Line   : in out SparkLex.Line_Context;
                             New_Context : in     SparkLex.Program_Context);
      --# derives Curr_Line from *,
      --#                        New_Context;
      --# post 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.Lookahead_Pos = Curr_Line~.Lookahead_Pos and
      --#   Curr_Line.Last_Token_Pos = Curr_Line~.Last_Token_Pos;

      procedure Set_Anno_Context (Curr_Line   : in out SparkLex.Line_Context;
                                  New_Context : in     SparkLex.Annotation_Context);
      --# derives Curr_Line from *,
      --#                        New_Context;
      pragma Unreferenced (Set_Anno_Context); -- not used at present

   end LineManager;

   No_Of_RW      : constant Positive := 120;
   Min_RW_Length : constant Positive := 1;
   Max_RW_Length : constant Positive := 20;

   type Anno_Type is (Start_Anno, Proof_Anno, Hide_Anno, Other_Anno, No_Anno);

   subtype RW_Length is Positive range 1 .. Max_RW_Length;
   subtype RW_Index is Positive range 1 .. No_Of_RW;

   subtype Res_Word is String (RW_Length);
   type RWPair is record
      Word  : Res_Word;
      Token : SP_Symbols.SP_Terminal;
   end record;
   type RWList is array (RW_Index) of RWPair;

   -- The table of reserved words MUST be listed in alphabetical order.
   RW : constant RWList :=
     RWList'
     (RWPair'(Res_Word'("abort               "), SP_Symbols.RWabort),
      RWPair'(Res_Word'("abs                 "), SP_Symbols.RWabs),
      RWPair'(Res_Word'("abstract            "), SP_Symbols.RWabstract),
      RWPair'(Res_Word'("accept              "), SP_Symbols.RWaccept),
      RWPair'(Res_Word'("access              "), SP_Symbols.RWaccess),
      RWPair'(Res_Word'("aliased             "), SP_Symbols.RWaliased),
      RWPair'(Res_Word'("all                 "), SP_Symbols.RWall),
      RWPair'(Res_Word'("and                 "), SP_Symbols.RWand),
      RWPair'(Res_Word'("are_interchangeable "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("array               "), SP_Symbols.RWarray),
      RWPair'(Res_Word'("as                  "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("assert              "), SP_Symbols.RWassert),
      RWPair'(Res_Word'("assume              "), SP_Symbols.RWassume),
      RWPair'(Res_Word'("at                  "), SP_Symbols.RWat),

      RWPair'(Res_Word'("begin               "), SP_Symbols.RWbegin),
      RWPair'(Res_Word'("body                "), SP_Symbols.RWbody),

      RWPair'(Res_Word'("case                "), SP_Symbols.RWcase),
      RWPair'(Res_Word'("check               "), SP_Symbols.RWcheck),
      RWPair'(Res_Word'("const               "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("constant            "), SP_Symbols.RWconstant),

      RWPair'(Res_Word'("declare             "), SP_Symbols.RWdeclare),
      RWPair'(Res_Word'("delay               "), SP_Symbols.RWdelay),
      RWPair'(Res_Word'("delta               "), SP_Symbols.RWdelta),
      RWPair'(Res_Word'("derives             "), SP_Symbols.RWderives),
      RWPair'(Res_Word'("digits              "), SP_Symbols.RWdigits),
      RWPair'(Res_Word'("div                 "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("do                  "), SP_Symbols.RWdo),

      RWPair'(Res_Word'("element             "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("else                "), SP_Symbols.RWelse),
      RWPair'(Res_Word'("elsif               "), SP_Symbols.RWelsif),
      RWPair'(Res_Word'("end                 "), SP_Symbols.RWend),
      RWPair'(Res_Word'("entry               "), SP_Symbols.RWentry),
      RWPair'(Res_Word'("exception           "), SP_Symbols.RWexception),
      RWPair'(Res_Word'("exit                "), SP_Symbols.RWexit),

      RWPair'(Res_Word'("finish              "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("first               "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("for                 "), SP_Symbols.RWfor),
      RWPair'(Res_Word'("for_all             "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("for_some            "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("from                "), SP_Symbols.RWfrom),
      RWPair'(Res_Word'("function            "), SP_Symbols.RWfunction),

      RWPair'(Res_Word'("generic             "), SP_Symbols.RWgeneric),
      RWPair'(Res_Word'("global              "), SP_Symbols.RWglobal),
      RWPair'(Res_Word'("goal                "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("goto                "), SP_Symbols.RWgoto),

      RWPair'(Res_Word'("hide                "), SP_Symbols.RWhide),

      RWPair'(Res_Word'("if                  "), SP_Symbols.RWif),
      RWPair'(Res_Word'("in                  "), SP_Symbols.RWin),
      RWPair'(Res_Word'("inherit             "), SP_Symbols.RWinherit),
      RWPair'(Res_Word'("initializes         "), SP_Symbols.RWinitializes),
      RWPair'(Res_Word'("interface           "), SP_Symbols.RWinterface),
      RWPair'(Res_Word'("is                  "), SP_Symbols.RWis),

      RWPair'(Res_Word'("last                "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("limited             "), SP_Symbols.RWlimited),
      RWPair'(Res_Word'("loop                "), SP_Symbols.RWloop),

      RWPair'(Res_Word'("main_program        "), SP_Symbols.RWmain_program),
      RWPair'(Res_Word'("may_be_deduced      "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("may_be_deduced_from "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("may_be_replaced_by  "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("mod                 "), SP_Symbols.RWmod),

      RWPair'(Res_Word'("new                 "), SP_Symbols.RWnew),
      RWPair'(Res_Word'("nonfirst            "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("nonlast             "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("not                 "), SP_Symbols.RWnot),
      RWPair'(Res_Word'("not_in              "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("null                "), SP_Symbols.RWnull),

      RWPair'(Res_Word'("odd                 "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("of                  "), SP_Symbols.RWof),
      RWPair'(Res_Word'("or                  "), SP_Symbols.RWor),
      RWPair'(Res_Word'("others              "), SP_Symbols.RWothers),
      RWPair'(Res_Word'("out                 "), SP_Symbols.RWout),
      RWPair'(Res_Word'("overriding          "), SP_Symbols.RWoverriding),
      RWPair'(Res_Word'("own                 "), SP_Symbols.RWown),

      RWPair'(Res_Word'("package             "), SP_Symbols.RWpackage),
      RWPair'(Res_Word'("pending             "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("post                "), SP_Symbols.RWpost),
      RWPair'(Res_Word'("pragma              "), SP_Symbols.RWpragma),
      RWPair'(Res_Word'("pre                 "), SP_Symbols.RWpre),
      RWPair'(Res_Word'("pred                "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("private             "), SP_Symbols.RWprivate),
      RWPair'(Res_Word'("procedure           "), SP_Symbols.RWprocedure),
      RWPair'(Res_Word'("proof               "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("protected           "), SP_Symbols.RWprotected),

      RWPair'(Res_Word'("raise               "), SP_Symbols.RWraise),
      RWPair'(Res_Word'("range               "), SP_Symbols.RWrange),
      RWPair'(Res_Word'("real                "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("record              "), SP_Symbols.RWrecord),
      RWPair'(Res_Word'("rem                 "), SP_Symbols.RWrem),
      RWPair'(Res_Word'("renames             "), SP_Symbols.RWrenames),
      RWPair'(Res_Word'("requeue             "), SP_Symbols.RWrequeue),
      RWPair'(Res_Word'("requires            "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("return              "), SP_Symbols.RWreturn),
      RWPair'(Res_Word'("reverse             "), SP_Symbols.RWreverse),

      RWPair'(Res_Word'("save                "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("select              "), SP_Symbols.RWselect),
      RWPair'(Res_Word'("separate            "), SP_Symbols.RWseparate),
      RWPair'(Res_Word'("sequence            "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("set                 "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("some                "), SP_Symbols.RWsome),
      RWPair'(Res_Word'("sqr                 "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("start               "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("strict_subset_of    "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("subset_of           "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("subtype             "), SP_Symbols.RWsubtype),
      RWPair'(Res_Word'("succ                "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("synchronized        "), SP_Symbols.RWsynchronized),

      RWPair'(Res_Word'("tagged              "), SP_Symbols.RWtagged),
      RWPair'(Res_Word'("task                "), SP_Symbols.RWtask),
      RWPair'(Res_Word'("terminate           "), SP_Symbols.RWterminate),
      RWPair'(Res_Word'("then                "), SP_Symbols.RWthen),
      RWPair'(Res_Word'("type                "), SP_Symbols.RWtype),

      RWPair'(Res_Word'("until               "), SP_Symbols.RWuntil),
      RWPair'(Res_Word'("update              "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("use                 "), SP_Symbols.RWuse),

      RWPair'(Res_Word'("var                 "), SP_Symbols.predefined_FDL_identifier),

      RWPair'(Res_Word'("when                "), SP_Symbols.RWwhen),
      RWPair'(Res_Word'("where               "), SP_Symbols.predefined_FDL_identifier),
      RWPair'(Res_Word'("while               "), SP_Symbols.RWwhile),
      RWPair'(Res_Word'("with                "), SP_Symbols.RWwith),

      RWPair'(Res_Word'("xor                 "), SP_Symbols.RWxor));

         No_Of_FDL_RW : constant Positive := 34;
         subtype FDL_RW_Index is Positive range 1 .. No_Of_FDL_RW;
         type FDL_RWList is array (FDL_RW_Index) of Res_Word;

         FDL_RW : constant FDL_RWList :=
           FDL_RWList'
           (Res_Word'("are_interchangeable "),
            Res_Word'("as                  "),

            Res_Word'("const               "),

            Res_Word'("div                 "),

            Res_Word'("element             "),

            Res_Word'("finish              "),
            Res_Word'("first               "),
            Res_Word'("for_all             "),
            Res_Word'("for_some            "),

            Res_Word'("goal                "),
            Res_Word'("last                "),

            Res_Word'("may_be_deduced      "),
            Res_Word'("may_be_deduced_from "),
            Res_Word'("may_be_replaced_by  "),

            Res_Word'("nonfirst            "),
            Res_Word'("nonlast             "),
            Res_Word'("not_in              "),

            Res_Word'("odd                 "),

            Res_Word'("pending             "),
            Res_Word'("pred                "),
            Res_Word'("proof               "),

            Res_Word'("real                "),
            Res_Word'("requires            "),

            Res_Word'("save                "),
            Res_Word'("sequence            "),
            Res_Word'("set                 "),
            Res_Word'("sqr                 "),
            Res_Word'("start               "),
            Res_Word'("strict_subset_of    "),
            Res_Word'("subset_of           "),
            Res_Word'("succ                "),

            Res_Word'("update              "),

            Res_Word'("var                 "),

            Res_Word'("where               "));

         subtype Offset is Integer range 1 .. 4;
         type Possible_Prefixes is (Field_Prefix, Update_Prefix);
         type Prefix_Strings is array (Offset) of Character;
         type Prefix_Tables is array (Possible_Prefixes) of Prefix_Strings;
         Prefix_Table : constant Prefix_Tables :=
           Prefix_Tables'(Field_Prefix  => Prefix_Strings'('f', 'l', 'd', '_'),
                          Update_Prefix => Prefix_Strings'('u', 'p', 'f', '_'));

         package body LineManager is separate;

         --++++  The following functions are defined as per Ada LRM Chapter 2. ++++--
         -------------------------------------------------------------------------------

         -- Note a "Special Character" here to the SPARK Lexer is NOT the
         -- same as Ada95 LRM's Ada.Characters.Handling.Is_Special
         function Special_Character (Ch : Character) return Boolean is
            Result : Boolean;
         begin
            case Ch is
               when '"' |
                 '#' |
                 '&' |
                 ''' |
                 '(' |
                 ')' |
                 '*' |
                 '+' |
                 ',' |
                 '-' |
                 '.' |
                 '/' |
                 ':' |
                 ';' |
                 '<' |
                 '=' |
                 '>' |
                 '_' |
                 '|' =>
                  Result := True;
               when others =>
                  Result := False;
            end case;
            return Result;
         end Special_Character;

         function Format_Effector (Ch : Character) return Boolean is
            Result : Boolean;
         begin
            case Ch is
               when Ada.Characters.Latin_1.HT |
                 Ada.Characters.Latin_1.VT |
                 Ada.Characters.Latin_1.CR |
                 Ada.Characters.Latin_1.LF |
                 Ada.Characters.Latin_1.FF =>
                  Result := True;
               when others =>
                  Result := False;
            end case;
            return Result;
         end Format_Effector;

         function Other_Special_Character (Ch : Character) return Boolean is
            Result : Boolean;
         begin
            case Ch is
               when '!' | '$' | '%' | '?' | '@' | '[' | '\' | ']' | '^' | '`' | '{' | '}' | '~' =>
                  Result := True;
               when others =>
                  Result := False;
            end case;
            return Result;
         end Other_Special_Character;

         -- Note a "Basic Character" here to the SPARK Lexer is NOT the
         -- same as Ada95 LRM's Ada.Characters.Handling.Is_Basic
         function Basic_Graphic_Character (Ch : Character) return Boolean is
         begin
            return Ada.Characters.Handling.Is_Upper (Ch)
              or else Ada.Characters.Handling.Is_Digit (Ch)
              or else Special_Character (Ch => Ch)
              or else Ch = ' ';
         end Basic_Graphic_Character;

         -- Note a "Graphic Character" here to the SPARK Lexer is NOT the
         -- same as Ada95 LRM's Ada.Characters.Handling.Is_Graphic
         function Graphic_Character (Ch : Character) return Boolean is
         begin
            return Basic_Graphic_Character (Ch => Ch)
              or else Ada.Characters.Handling.Is_Lower (Ch)
              or else Other_Special_Character (Ch => Ch);
         end Graphic_Character;

         --  Same as Graphic_Character but also allows all extended
         --  ASCII characters (useful for string constants).
         function Extended_ASCII (Ch : Character) return Boolean is
         begin
            return Graphic_Character (Ch) or
              Character'Pos (Ch) >= 128;
         end Extended_ASCII;

         function Simple_Delimiter (Ch : Character) return Boolean is
            Result : Boolean;
         begin
            case Ch is
               when '&' |
                 ''' |
                 '(' |
                 ')' |
                 '*' |
                 '+' |
                 ',' |
                 '-' |
                 '.' |
                 '/' |
                 ':' |
                 ';' |
                 '<' |
                 '=' |
                 '>' |
                 '|' |
                 '[' |
                 ']' |
                 '@' |
                 '~' |
                 '%' =>
                  Result := True;
               when others =>
                  Result := False;
            end case;
            return Result;
         end Simple_Delimiter;

         function Letter_Or_Digit (Ch : Character) return Boolean
         --# return Ada.Characters.Handling.Is_Letter (Ch) or Ada.Characters.Handling.Is_Digit (Ch);
         is
         begin
            return Ada.Characters.Handling.Is_Letter (Item => Ch) or else Ada.Characters.Handling.Is_Digit (Item => Ch);
         end Letter_Or_Digit;

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

         procedure Clear_Line_Context
         --# post E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
         --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Last_Token_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
         is
         begin
            LineManager.Clear_Line (Curr_Line => Curr_Line);
         end Clear_Line_Context;

         procedure Store_Line_Context (File_Line : out Line_Context)
         --# pre E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
         --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Last_Token_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
         --# post E_Strings.Get_Length (File_Line.Conts) < Natural'Last and
         --#   File_Line.Curr_Pos <= E_Strings.Get_Length (File_Line.Conts) + 1 and
         --#   File_Line.Lookahead_Pos <= E_Strings.Get_Length (File_Line.Conts) + 1 and
         --#   File_Line.Last_Token_Pos <= E_Strings.Get_Length (File_Line.Conts) + 1;
         is
         begin
            File_Line :=
              Line_Context'
              (Context        => In_Ada,
               Anno_Context   => Start_Annotation,
               Line_No        => Curr_Line.Line_No,
               Last_Token_Pos => Curr_Line.Last_Token_Pos,
               Curr_Pos       => Curr_Line.Last_Token_Pos,
               Lookahead_Pos  => Curr_Line.Lookahead_Pos,
               Conts          => Curr_Line.Conts);
         end Store_Line_Context;

         procedure Restore_Line_Context (File_Line : in Line_Context)
         --# pre E_Strings.Get_Length (File_Line.Conts) < Natural'Last and
         --#   File_Line.Curr_Pos <= E_Strings.Get_Length (File_Line.Conts) + 1 and
         --#   File_Line.Lookahead_Pos <= E_Strings.Get_Length (File_Line.Conts) + 1 and
         --#   File_Line.Last_Token_Pos <= E_Strings.Get_Length (File_Line.Conts) + 1;
         --# post E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
         --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Last_Token_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
         is
         begin
            LineManager.Copy_In_Line (Line      => File_Line,
                                      Curr_Line => Curr_Line);
         end Restore_Line_Context;

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

         procedure Check_Reserved
           (Curr_Line          : in     Line_Context;
            Start_Pos, End_Pos : in     E_Strings.Positions;
            Look_Ahead         : in     Boolean;
            Token              :    out SP_Symbols.SP_Terminal)
         --# global in     CommandLineData.Content;
         --#        in     Dictionary.Dict;
         --#        in     LexTokenManager.State;
         --#        in out ErrorHandler.Error_Context;
         --#        in out SPARK_IO.File_Sys;
         --# derives ErrorHandler.Error_Context,
         --#         SPARK_IO.File_Sys          from CommandLineData.Content,
         --#                                         Curr_Line,
         --#                                         Dictionary.Dict,
         --#                                         End_Pos,
         --#                                         ErrorHandler.Error_Context,
         --#                                         LexTokenManager.State,
         --#                                         Look_Ahead,
         --#                                         SPARK_IO.File_Sys,
         --#                                         Start_Pos &
         --#         Token                      from CommandLineData.Content,
         --#                                         Curr_Line,
         --#                                         End_Pos,
         --#                                         Start_Pos;
         --# pre E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
         --#   Start_Pos <= E_Strings.Get_Length (Curr_Line.Conts) and
         --#   End_Pos <= E_Strings.Get_Length (Curr_Line.Conts) and
         --#   Start_Pos <= End_Pos;
         is
            type Cmp_Res is (CEQ, CLT, CGT);
            Leng    : Positive;
            Ix      : RW_Index;
            IL, IU  : Positive;
            Result  : Cmp_Res;
            L_Token : SP_Symbols.SP_Terminal;

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

            function Comp_RW
              (Curr_Line          : Line_Context;
               R_Word             : Res_Word;
               Start_Pos, End_Pos : E_Strings.Positions)
              return               Cmp_Res
            --# pre Start_Pos <= End_Pos;
            is
               Ch1, Ch2    : Character;
               RW_X        : RW_Length;
               Comp_Result : Cmp_Res;
            begin
               RW_X := 1;

               loop
                  --# assert RW_X + Start_Pos - 1 <= End_Pos and
                  --#   RW_X <= Max_RW_Length;
                  Ch1 := R_Word (RW_X);

                  -- Reserved words in lower case.
                  Ch2 :=
                    Ada.Characters.Handling.To_Lower
                    (Item => E_Strings.Get_Element (E_Str => Curr_Line.Conts,
                                                    Pos   => RW_X + (Start_Pos - 1)));
                  if Ch1 < Ch2 then
                     Comp_Result := CLT;
                  elsif Ch1 > Ch2 then
                     Comp_Result := CGT;
                  elsif RW_X + (Start_Pos - 1) = End_Pos then
                     if RW_X = Max_RW_Length or else R_Word (RW_X + 1) = ' ' then
                        Comp_Result := CEQ;
                     else
                        Comp_Result := CGT;
                     end if;
                  else
                     Comp_Result := CEQ;
                  end if;
                  exit when Comp_Result /= CEQ or else RW_X + (Start_Pos - 1) >= End_Pos or else RW_X = Max_RW_Length;
                  RW_X := RW_X + 1;
               end loop;
               return Comp_Result;
            end Comp_RW;

            function Check_FLD_Or_UPF
              (Curr_Line : Line_Context;
               Leng      : Positive;
               Start_Pos : E_Strings.Positions)
              return      SP_Symbols.SP_Terminal
            --# pre E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
            --#   Start_Pos <= E_Strings.Get_Length (Curr_Line.Conts);
            is
               Prefix_Sort : Possible_Prefixes;
               Result      : SP_Symbols.SP_Terminal;

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

               function Check_Rest_Of_Prefix
                 (Curr_Line   : Line_Context;
                  Prefix_Sort : Possible_Prefixes;
                  Start_Pos   : E_Strings.Positions)
                 return        SP_Symbols.SP_Terminal
               --# pre E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
               --#   Start_Pos <= E_Strings.Get_Length (Curr_Line.Conts);
               is
                  Ptr    : Offset;
                  Result : SP_Symbols.SP_Terminal;
               begin
                  Ptr := 1;
                  loop
                     --# assert E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
                     --#   Start_Pos + Ptr <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
                     --#   Ptr in Offset;
                     if Start_Pos + Ptr > E_Strings.Get_Length (E_Str => Curr_Line.Conts)
                       or else Ada.Characters.Handling.To_Lower
                       (E_Strings.Get_Element (E_Str => Curr_Line.Conts,
                                               Pos   => Start_Pos + (Ptr - 1))) /=
                       Prefix_Table (Prefix_Sort) (Ptr) then
                        Result := SP_Symbols.identifier;
                        exit;
                     end if;

                     if Ptr = Offset'Last then
                        Result := SP_Symbols.predefined_FDL_identifier;
                        exit;
                     end if;

                     Ptr := Ptr + 1;
                  end loop;
                  return Result;
               end Check_Rest_Of_Prefix;

            begin -- Check_FLD_Or_UPF
               Result := SP_Symbols.identifier;
               if Leng >= 5 then -- minimum length a valid fld_ or upf_ could be
                  if Ada.Characters.Handling.To_Lower (E_Strings.Get_Element (E_Str => Curr_Line.Conts,
                                                                              Pos   => Start_Pos)) = 'f' then
                     Prefix_Sort := Field_Prefix;
                     Result      := Check_Rest_Of_Prefix (Curr_Line   => Curr_Line,
                                                          Prefix_Sort => Prefix_Sort,
                                                          Start_Pos   => Start_Pos);
                  elsif Ada.Characters.Handling.To_Lower (E_Strings.Get_Element (E_Str => Curr_Line.Conts,
                                                                                 Pos   => Start_Pos)) =
                    'u' then
                     Prefix_Sort := Update_Prefix;
                     Result      := Check_Rest_Of_Prefix (Curr_Line   => Curr_Line,
                                                          Prefix_Sort => Prefix_Sort,
                                                          Start_Pos   => Start_Pos);
                  else
                     Result := SP_Symbols.identifier;
                  end if;
               end if;
               return Result;
            end Check_FLD_Or_UPF;

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

            function Convert_FDL (Token : SP_Symbols.SP_Terminal) return SP_Symbols.SP_Terminal
            --# global in CommandLineData.Content;
            is
               Result : SP_Symbols.SP_Terminal;
            begin
               if Token = SP_Symbols.predefined_FDL_identifier and then not CommandLineData.Content.FDL_Reserved then
                  Result := SP_Symbols.identifier;
               else
                  Result := Token;
               end if;
               return Result;
            end Convert_FDL;

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

            function Convert_Reserved (Token : SP_Symbols.SP_Terminal) return SP_Symbols.SP_Terminal
            --# global in CommandLineData.Content;
            is
               Result : SP_Symbols.SP_Terminal;
            begin
               if Token = SP_Symbols.RWsome and then not CommandLineData.Content.FDL_Reserved then
                  Result := SP_Symbols.identifier;
               else
                  Result := Token;
               end if;
               return Result;
            end Convert_Reserved;

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

            procedure Convert95_And_2005_Reserved
              (Curr_Line       : in     Line_Context;
               Token           : in     SP_Symbols.SP_Terminal;
               Start_Pos       : in     E_Strings.Positions;
               Look_Ahead      : in     Boolean;
               Converted_Token :    out SP_Symbols.SP_Terminal)
            --# global in     CommandLineData.Content;
            --#        in     Dictionary.Dict;
            --#        in     LexTokenManager.State;
            --#        in out ErrorHandler.Error_Context;
            --#        in out SPARK_IO.File_Sys;
            --# derives Converted_Token            from CommandLineData.Content,
            --#                                         Token &
            --#         ErrorHandler.Error_Context,
            --#         SPARK_IO.File_Sys          from CommandLineData.Content,
            --#                                         Curr_Line,
            --#                                         Dictionary.Dict,
            --#                                         ErrorHandler.Error_Context,
            --#                                         LexTokenManager.State,
            --#                                         Look_Ahead,
            --#                                         SPARK_IO.File_Sys,
            --#                                         Start_Pos,
            --#                                         Token;
            is
               Result : SP_Symbols.SP_Terminal;

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

               -- Returns true if Token is a new 95 reserved word.
               function SPARK95_Reserved (Token : SP_Symbols.SP_Terminal) return Boolean
               --# global in CommandLineData.Content;
               is
               begin
                  -- In SPARK83 mode, "aliased", "protected", "requeue", "tagged",
                  -- "until" are all identifiers, not reserved words.  Additionally,
                  -- "abstract" is an identifier UNLESS -fdl is active, in which case
                  -- it remains a reserved word
                  return Token = SP_Symbols.RWaliased
                    or else Token = SP_Symbols.RWprotected
                    or else Token = SP_Symbols.RWrequeue
                    or else Token = SP_Symbols.RWtagged
                    or else Token = SP_Symbols.RWuntil
                    or else (Token = SP_Symbols.RWabstract and then not CommandLineData.Content.FDL_Reserved);
               end SPARK95_Reserved;

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

               -- Returns true if Token is a new 2005 reserved word.
               function SPARK2005_Reserved (Token : SP_Symbols.SP_Terminal) return Boolean is
               begin
                  return Token = SP_Symbols.RWinterface
                    or else Token = SP_Symbols.RWoverriding
                    or else Token = SP_Symbols.RWsynchronized;
               end SPARK2005_Reserved;

            begin -- Convert95_And_2005_Reserved
               case CommandLineData.Content.Language_Profile is
                  when CommandLineData.SPARK83 =>
                     if SPARK95_Reserved (Token => Token) or else SPARK2005_Reserved (Token => Token) then
                        Result := SP_Symbols.identifier;
                     else
                        Result := Token;
                     end if;
                  when CommandLineData.SPARK95 =>
                     if SPARK2005_Reserved (Token => Token) then
                        Result := SP_Symbols.identifier;

                        if not Look_Ahead then

                           -- Raise a warning if the identifier is an Ada2005
                           -- reserve word. To remove duplicate warning messages,
                           -- lexical analyser only raises the warning if it is
                           -- not looking ahead.

                           ErrorHandler.Semantic_Warning
                             (Err_Num  => 7,
                              Position => LexTokenManager.Token_Position'(Start_Line_No => Curr_Line.Line_No,
                                                                          Start_Pos     => Start_Pos),
                              Id_Str   => LexTokenManager.Null_String);
                        end if;
                     else
                        Result := Token;
                     end if;
                  when CommandLineData.SPARK2005_Profiles =>
                     Result := Token;
               end case;

               Converted_Token := Result;
            end Convert95_And_2005_Reserved;

         begin -- Check_Reserved
            Leng := (End_Pos - Start_Pos) + 1;
            if Leng <= Max_RW_Length then
               IL := RW_Index'First;
               IU := RW_Index'Last;
               loop
                  Ix     := IL;
                  Result := Comp_RW (Curr_Line => Curr_Line,
                                     R_Word    => RW (Ix).Word,
                                     Start_Pos => Start_Pos,
                                     End_Pos   => End_Pos);
                  exit when Result = CGT or else Result = CEQ;
                  Ix     := IU;
                  Result := Comp_RW (Curr_Line => Curr_Line,
                                     R_Word    => RW (Ix).Word,
                                     Start_Pos => Start_Pos,
                                     End_Pos   => End_Pos);
                  exit when Result = CLT or else Result = CEQ;

                  Ix     := (IL + IU) / 2;
                  Result := Comp_RW (Curr_Line => Curr_Line,
                                     R_Word    => RW (Ix).Word,
                                     Start_Pos => Start_Pos,
                                     End_Pos   => End_Pos);
                  case Result is
                     when CEQ =>
                        null;
                     when CGT =>
                        if IL < RW_Index'Last then
                           IL := IL + 1;
                        end if;
                        IU := Ix - 1;
                     when CLT =>
                        IL := Ix + 1;
                        if IU > RW_Index'First then
                           IU := IU - 1;
                        end if;
                  end case;
                  exit when Result = CEQ or else IL > IU;
                  --# assert IL <= IU and IL in RW_Index and IU in RW_Index and Leng >= Min_RW_Length and
                  --#   Start_Pos <= End_Pos and Ix in RW_Index and Leng <= End_Pos;
               end loop;

               if Result = CEQ then
                  L_Token := RW (Ix).Token;
               else
                  L_Token := Check_FLD_Or_UPF (Curr_Line => Curr_Line,
                                               Leng      => Leng,
                                               Start_Pos => Start_Pos);
               end if;
            else
               L_Token := Check_FLD_Or_UPF (Curr_Line => Curr_Line,
                                            Leng      => Leng,
                                            Start_Pos => Start_Pos);
            end if;

            Convert95_And_2005_Reserved
              (Curr_Line       => Curr_Line,
               Token           => Convert_Reserved (Token => Convert_FDL (Token => L_Token)),
               Start_Pos       => Start_Pos,
               Look_Ahead      => Look_Ahead,
               Converted_Token => Token);
         end Check_Reserved;

         function Check_FDL_RW (Ex_Str : E_Strings.T) return Boolean is
            type Cmp_Res is (CEQ, CLT, CGT);
            Is_FDL_RW : Boolean := True;
            Ix        : FDL_RW_Index;
            IL, IU    : Positive;
            Result    : Cmp_Res;

            function Comp_FDL_RW (R_Word : Res_Word;
                                  Ex_Str : E_Strings.T) return Cmp_Res
            --# pre E_Strings.Get_Length (Ex_Str) >= 1;
            is
               Ch1, Ch2    : Character;
               RW_X        : RW_Length;
               Comp_Result : Cmp_Res;
            begin
               RW_X := 1;

               loop
                  --# assert RW_X <= E_Strings.Get_Length (Ex_Str) and
                  --#   RW_X <= Max_RW_Length;
                  Ch1 := R_Word (RW_X);

                  -- Reserved words in lower case.
                  Ch2 := Ada.Characters.Handling.To_Lower (Item => E_Strings.Get_Element (E_Str => Ex_Str,
                                                                                          Pos   => RW_X));
                  if Ch1 < Ch2 then
                     Comp_Result := CLT;
                  elsif Ch1 > Ch2 then
                     Comp_Result := CGT;
                  elsif RW_X = E_Strings.Get_Length (E_Str => Ex_Str) then
                     if RW_X = Max_RW_Length or else R_Word (RW_X + 1) = ' ' then
                        Comp_Result := CEQ;
                     else
                        Comp_Result := CGT;
                     end if;
                  else
                     Comp_Result := CEQ;
                  end if;
                  exit when Comp_Result /= CEQ or else RW_X = E_Strings.Get_Length (E_Str => Ex_Str) or else RW_X = Max_RW_Length;
                  RW_X := RW_X + 1;
               end loop;
               return Comp_Result;
            end Comp_FDL_RW;

         begin -- Check_FDL_RW
            if E_Strings.Get_Length (E_Str => Ex_Str) >= Min_RW_Length
              and then E_Strings.Get_Length (E_Str => Ex_Str) <= Max_RW_Length then
               IL := FDL_RW_Index'First;
               IU := FDL_RW_Index'Last;
               loop
                  Result := Comp_FDL_RW (R_Word => FDL_RW (IL),
                                         Ex_Str => Ex_Str);
                  exit when Result = CGT or else Result = CEQ;
                  Result := Comp_FDL_RW (R_Word => FDL_RW (IU),
                                         Ex_Str => Ex_Str);
                  exit when Result = CLT or else Result = CEQ;

                  Ix     := (IL + IU) / 2;
                  Result := Comp_FDL_RW (R_Word => FDL_RW (Ix),
                                         Ex_Str => Ex_Str);
                  case Result is
                     when CEQ =>
                        null;
                     when CGT =>
                        if IL < FDL_RW_Index'Last then
                           IL := IL + 1;
                        end if;
                        IU := Ix - 1;
                     when CLT =>
                        IL := Ix + 1;
                        if IU > FDL_RW_Index'First then
                           IU := IU - 1;
                        end if;
                  end case;
                  exit when Result = CEQ or else IL > IU;
                  --# assert IL <= IU and
                  --#   IL in FDL_RW_Index and
                  --#   IU in FDL_RW_Index and
                  --#   E_Strings.Get_Length (Ex_Str) >= Min_RW_Length;
               end loop;

               if Result /= CEQ then
                  Is_FDL_RW := False;
               end if;
            else
               Is_FDL_RW := False;
            end if;
            return Is_FDL_RW;
         end Check_FDL_RW;

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

         --  Returns the type of the annotation starting at the lookahead position in
         --  the line buffer.
         procedure Check_Anno_Type (Curr_Line       : in     Line_Context;
                                    Unfinished_Anno : in     Boolean;
                                    Anno_Kind       :    out Anno_Type)
         --# global in     CommandLineData.Content;
         --#        in     Dictionary.Dict;
         --#        in     LexTokenManager.State;
         --#        in out ErrorHandler.Error_Context;
         --#        in out SPARK_IO.File_Sys;
         --# derives Anno_Kind                  from CommandLineData.Content,
         --#                                         Curr_Line,
         --#                                         Unfinished_Anno &
         --#         ErrorHandler.Error_Context,
         --#         SPARK_IO.File_Sys          from CommandLineData.Content,
         --#                                         Curr_Line,
         --#                                         Dictionary.Dict,
         --#                                         ErrorHandler.Error_Context,
         --#                                         LexTokenManager.State,
         --#                                         SPARK_IO.File_Sys;
         --# pre E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
         --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Last_Token_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
         is
            type Element_Type is (End_Of_Line_Or_Text, Non_Character, RW_Or_Ident);

            Unused, Start_Posn, End_Posn : E_Strings.Positions;
            Anno_Token                   : SP_Symbols.SP_Terminal;
            Next_Element                 : Element_Type;

            procedure Check_Next_Element
              (Curr_Line    : in     Line_Context;
               Start_Posn   : in     E_Strings.Positions;
               End_Posn     :    out E_Strings.Positions;
               Next_Element :    out Element_Type;
               Symbol       :    out SP_Symbols.SP_Terminal)
            --# global in     CommandLineData.Content;
            --#        in     Dictionary.Dict;
            --#        in     LexTokenManager.State;
            --#        in out ErrorHandler.Error_Context;
            --#        in out SPARK_IO.File_Sys;
            --# derives End_Posn,
            --#         Next_Element               from Curr_Line,
            --#                                         Start_Posn &
            --#         ErrorHandler.Error_Context,
            --#         SPARK_IO.File_Sys          from CommandLineData.Content,
            --#                                         Curr_Line,
            --#                                         Dictionary.Dict,
            --#                                         ErrorHandler.Error_Context,
            --#                                         LexTokenManager.State,
            --#                                         SPARK_IO.File_Sys,
            --#                                         Start_Posn &
            --#         Symbol                     from CommandLineData.Content,
            --#                                         Curr_Line,
            --#                                         Start_Posn;
            --# pre E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
            --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
            --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
            --#   Start_Posn <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
            --#   Curr_Line.Last_Token_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
            --# post End_Posn <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
            is
               Ch                               : Character := ' ';
               Local_End_Posn, Local_Start_Posn : E_Strings.Positions;
            begin
               Local_Start_Posn := Start_Posn;
               loop
                  --# assert Local_Start_Posn <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
                  exit when Local_Start_Posn > E_Strings.Get_Length (E_Str => Curr_Line.Conts);
                  Ch := E_Strings.Get_Element (E_Str => Curr_Line.Conts,
                                               Pos   => Local_Start_Posn);
                  exit when Ch = End_Of_Text or else not LineManager.Separator (Ch => Ch);
                  Local_Start_Posn := Local_Start_Posn + 1;
               end loop;
               Local_End_Posn := Local_Start_Posn;
               if Local_Start_Posn <= E_Strings.Get_Length (E_Str => Curr_Line.Conts) then
                  if Ch = End_Of_Text then
                     Next_Element := End_Of_Line_Or_Text;
                     Symbol       := SP_Symbols.illegal_id; -- not used
                  elsif Letter_Or_Digit (Ch => Ch) then
                     if Ada.Characters.Handling.Is_Letter (Item => Ch) then
                        Next_Element := RW_Or_Ident;

                        -- Scan the next identifier, but then see if it's a reserved word.
                        while Local_End_Posn <= E_Strings.Get_Length (E_Str => Curr_Line.Conts)
                          and then (Letter_Or_Digit (Ch => E_Strings.Get_Element (E_Str => Curr_Line.Conts,
                                                                                  Pos   => Local_End_Posn))
                                      or else E_Strings.Get_Element (E_Str => Curr_Line.Conts,
                                                                     Pos   => Local_End_Posn) = '_') loop
                           --# assert Local_End_Posn <= E_Strings.Get_Length (Curr_Line.Conts) and
                           --#   Local_Start_Posn <= Local_End_Posn;
                           Local_End_Posn := Local_End_Posn + 1;
                        end loop;

                        Local_End_Posn := Local_End_Posn - 1;
                        Check_Reserved
                          (Curr_Line  => Curr_Line,
                           Start_Pos  => Local_Start_Posn,
                           End_Pos    => Local_End_Posn,
                           Look_Ahead => True,
                           Token      => Symbol);
                     else
                        Next_Element := Non_Character;
                        Symbol       := SP_Symbols.illegal_id;  -- not used
                     end if;
                  else
                     Next_Element := Non_Character;
                     Symbol       := SP_Symbols.illegal_id;  -- not used
                  end if;
               else
                  Next_Element := End_Of_Line_Or_Text;
                  Symbol       := SP_Symbols.illegal_id; -- not used
               end if;
               End_Posn := Local_End_Posn;

            end Check_Next_Element;

         begin -- Check_Anno_Type
            if Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (E_Str => Curr_Line.Conts) then
               Start_Posn := Curr_Line.Lookahead_Pos + 1;

               Check_Next_Element
                 (Curr_Line    => Curr_Line,
                  Start_Posn   => Start_Posn,
                  End_Posn     => End_Posn,
                  Next_Element => Next_Element,
                  Symbol       => Anno_Token);

               if Next_Element = End_Of_Line_Or_Text then
                  Anno_Kind := No_Anno;
               elsif Next_Element = Non_Character then
                  Anno_Kind := Other_Anno;
               else
                  case Anno_Token is
                     when SP_Symbols.RWmain_program |
                       SP_Symbols.RWinherit      |
                       SP_Symbols.RWown          |
                       SP_Symbols.RWinitializes  |
                       SP_Symbols.RWglobal       |
                       SP_Symbols.RWderives      |
                       SP_Symbols.RWdeclare      |
                       SP_Symbols.RWpre          |
                       SP_Symbols.RWpost         =>
                        Anno_Kind := Start_Anno;
                     when SP_Symbols.RWreturn =>
                        if (Unfinished_Anno) then
                           -- Still in an annotation so it's not the start of a new one
                           Anno_Kind := Other_Anno;
                        else
                           -- New annotation
                           Anno_Kind := Start_Anno;
                        end if;
                     when SP_Symbols.RWassert   |
                       SP_Symbols.RWassume   |
                       SP_Symbols.RWcheck    |
                       SP_Symbols.RWtype     |
                       SP_Symbols.RWsubtype  |
                       SP_Symbols.RWfunction |
                       SP_Symbols.RWaccept   |
                       SP_Symbols.RWend      =>
                        Anno_Kind := Proof_Anno;
                     when SP_Symbols.RWfor =>
                        -- do a second look ahead to check for "some" or "all"
                        if End_Posn <= E_Strings.Get_Length (E_Str => Curr_Line.Conts) then
                           Start_Posn := End_Posn + 1;
                           --# accept F, 10, Unused, "Unused not referenced here";
                           Check_Next_Element
                             (Curr_Line    => Curr_Line,
                              Start_Posn   => Start_Posn,
                              End_Posn     => Unused,
                              Next_Element => Next_Element,
                              Symbol       => Anno_Token);
                           --# end accept;
                           if Next_Element = RW_Or_Ident
                             and then (Anno_Token = SP_Symbols.RWsome or else Anno_Token = SP_Symbols.RWall) then
                              Anno_Kind := Other_Anno;
                           else
                              Anno_Kind := Proof_Anno;
                           end if;
                        else
                           Anno_Kind := Proof_Anno;
                        end if;
                     when SP_Symbols.RWhide =>
                        Anno_Kind := Hide_Anno;
                     when others =>
                        -- When a proof constant declaration occurs
                        -- interpreting --# as proof context is
                        -- handled by Hyph_Intro.
                        Anno_Kind := Other_Anno;
                  end case;
               end if;
            else
               Anno_Kind := No_Anno;
            end if;
            --# accept F, 33, Unused, "Unused not referenced here";
         end Check_Anno_Type;

         -- Main implementation of the lexical analyser, common to both
         -- the Examiner and SPARKFormat.
         procedure Lex
           (Prog_Text    : in     SPARK_IO.File_Type;
            Allow_Dollar : in     Boolean;
            Curr_Line    : in out Line_Context;
            Token        :    out SP_Symbols.SP_Terminal;
            Lex_Val      :    out LexTokenManager.Lex_Value;
            Punct_Token  :    out Boolean)
         --# global in     CommandLineData.Content;
         --#        in     Dictionary.Dict;
         --#        in out ErrorHandler.Error_Context;
         --#        in out LexTokenManager.State;
         --#        in out SPARK_IO.File_Sys;
         --# derives Curr_Line,
         --#         ErrorHandler.Error_Context,
         --#         LexTokenManager.State,
         --#         Lex_Val,
         --#         Punct_Token,
         --#         SPARK_IO.File_Sys,
         --#         Token                      from Allow_Dollar,
         --#                                         CommandLineData.Content,
         --#                                         Curr_Line,
         --#                                         Dictionary.Dict,
         --#                                         ErrorHandler.Error_Context,
         --#                                         LexTokenManager.State,
         --#                                         Prog_Text,
         --#                                         SPARK_IO.File_Sys;
         --# pre E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
         --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Last_Token_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
         --# post E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
         --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Last_Token_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
            is separate;

         procedure Examiner_Lex
           (Prog_Text   : in     SPARK_IO.File_Type;
            Token       :    out SP_Symbols.SP_Terminal;
            Lex_Val     :    out LexTokenManager.Lex_Value;
            Punct_Token :    out Boolean)
         --# pre E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
         --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Last_Token_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
         --# post E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
         --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Last_Token_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
         is
         begin
            Lex
              (Prog_Text    => Prog_Text,
               Allow_Dollar => False,
               Curr_Line    => Curr_Line,
               Token        => Token,
               Lex_Val      => Lex_Val,
               Punct_Token  => Punct_Token);
         end Examiner_Lex;

         procedure SPARK_Format_Lex
           (Prog_Text   : in     SPARK_IO.File_Type;
            Token       :    out SP_Symbols.SP_Terminal;
            Lex_Val     :    out LexTokenManager.Lex_Value;
            Punct_Token :    out Boolean)
         --# pre E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
         --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Last_Token_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
         --# post E_Strings.Get_Length (Curr_Line.Conts) < Natural'Last and
         --#   Curr_Line.Curr_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Lookahead_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1 and
         --#   Curr_Line.Last_Token_Pos <= E_Strings.Get_Length (Curr_Line.Conts) + 1;
         is
         begin
            -- For SPARKFormat, we allow identifiers to begin with a '$'
            -- character so that SPARKFormat doesn't mess up annotations
            -- containing GNATPREP symbols
            Lex
              (Prog_Text    => Prog_Text,
               Allow_Dollar => True,
               Curr_Line    => Curr_Line,
               Token        => Token,
               Lex_Val      => Lex_Val,
               Punct_Token  => Punct_Token);
         end SPARK_Format_Lex;

         function Similar_Tokens (Token1, Token2 : SP_Symbols.SP_Terminal) return Boolean is
            type Token_Type is (
                                Delimiter,
                                Reserved_Word,
                                Id,
                                Number,
                                Chars,
                                Other_Token);

            Token_Type1, Token_Type2 : Token_Type;
            Result                   : Boolean;

            function Type_Of_Token (Token : SP_Symbols.SP_Terminal) return Token_Type is
               Token_Kind : Token_Type;
            begin
               case Token is
                  when SP_Symbols.ampersand         |
                    SP_Symbols.apostrophe        |
                    SP_Symbols.left_paren        |
                    SP_Symbols.right_paren       |
                    SP_Symbols.multiply          |
                    SP_Symbols.plus              |
                    SP_Symbols.comma             |
                    SP_Symbols.minus             |
                    SP_Symbols.point             |
                    SP_Symbols.divide            |
                    SP_Symbols.colon             |
                    SP_Symbols.semicolon         |
                    SP_Symbols.less_than         |
                    SP_Symbols.equals            |
                    SP_Symbols.greater_than      |
                    SP_Symbols.vertical_bar      |
                    SP_Symbols.arrow             |
                    SP_Symbols.double_dot        |
                    SP_Symbols.double_star       |
                    SP_Symbols.becomes           |
                    SP_Symbols.not_equal         |
                    SP_Symbols.greater_or_equal  |
                    SP_Symbols.less_or_equal     |
                    SP_Symbols.left_label_paren  |
                    SP_Symbols.right_label_paren |
                    SP_Symbols.box               |
                    SP_Symbols.implies           |
                    SP_Symbols.is_equivalent_to  |
                    SP_Symbols.tilde             |
                    SP_Symbols.square_open       |
                    SP_Symbols.square_close      |
                    SP_Symbols.percent           =>
                     Token_Kind := Delimiter;
                  when SP_Symbols.integer_number | SP_Symbols.real_number | SP_Symbols.based_integer | SP_Symbols.based_real =>
                     Token_Kind := Number;
                  when SP_Symbols.character_literal | SP_Symbols.string_literal =>
                     Token_Kind := Chars;
                  when SP_Symbols.identifier =>
                     Token_Kind := Id;
                  when SP_Symbols.RWabort        |
                    SP_Symbols.RWabs          |
                    SP_Symbols.RWabstract     |
                    SP_Symbols.RWaccept       |
                    SP_Symbols.RWaccess       |
                    SP_Symbols.RWaliased      |
                    SP_Symbols.RWall          |
                    SP_Symbols.RWand          |
                    SP_Symbols.RWandthen      |
                    SP_Symbols.RWany          |
                    SP_Symbols.RWarray        |
                    SP_Symbols.RWassert       |
                    SP_Symbols.RWassume       |
                    SP_Symbols.RWat           |
                    SP_Symbols.RWbegin        |
                    SP_Symbols.RWbody         |
                    SP_Symbols.RWcase         |
                    SP_Symbols.RWcheck        |
                    SP_Symbols.RWconstant     |
                    SP_Symbols.RWdeclare      |
                    SP_Symbols.RWdelay        |
                    SP_Symbols.RWdelta        |
                    SP_Symbols.RWderives      |
                    SP_Symbols.RWdigits       |
                    SP_Symbols.RWdo           |
                    SP_Symbols.RWelse         |
                    SP_Symbols.RWelsif        |
                    SP_Symbols.RWend          |
                    SP_Symbols.RWentry        |
                    SP_Symbols.RWexception    |
                    SP_Symbols.RWexit         |
                    SP_Symbols.RWfor          |
                    SP_Symbols.RWforall       |
                    SP_Symbols.RWforsome      |
                    SP_Symbols.RWfrom         |
                    SP_Symbols.RWfunction     |
                    SP_Symbols.RWgeneric      |
                    SP_Symbols.RWglobal       |
                    SP_Symbols.RWgoto         |
                    SP_Symbols.RWhide         |
                    SP_Symbols.RWif           |
                    SP_Symbols.RWin           |
                    SP_Symbols.RWinherit      |
                    SP_Symbols.RWinitializes  |
                    SP_Symbols.RWis           |
                    SP_Symbols.RWlimited      |
                    SP_Symbols.RWloop         |
                    SP_Symbols.RWmain_program |
                    SP_Symbols.RWmod          |
                    SP_Symbols.RWnew          |
                    SP_Symbols.RWnot          |
                    SP_Symbols.RWnotin        |
                    SP_Symbols.RWnull         |
                    SP_Symbols.RWof           |
                    SP_Symbols.RWor           |
                    SP_Symbols.RWorelse       |
                    SP_Symbols.RWothers       |
                    SP_Symbols.RWout          |
                    SP_Symbols.RWown          |
                    SP_Symbols.RWpackage      |
                    SP_Symbols.RWpost         |
                    SP_Symbols.RWpragma       |
                    SP_Symbols.RWpre          |
                    SP_Symbols.RWprivate      |
                    SP_Symbols.RWprotected    |
                    SP_Symbols.RWprocedure    |
                    SP_Symbols.RWraise        |
                    SP_Symbols.RWrange        |
                    SP_Symbols.RWrecord       |
                    SP_Symbols.RWrequeue      |
                    SP_Symbols.RWrem          |
                    SP_Symbols.RWrenames      |
                    SP_Symbols.RWreturn       |
                    SP_Symbols.RWreverse      |
                    SP_Symbols.RWselect       |
                    SP_Symbols.RWseparate     |
                    SP_Symbols.RWsome         |
                    SP_Symbols.RWsubtype      |
                    SP_Symbols.RWtagged       |
                    SP_Symbols.RWtask         |
                    SP_Symbols.RWterminate    |
                    SP_Symbols.RWthen         |
                    SP_Symbols.RWtype         |
                    SP_Symbols.RWuntil        |
                    SP_Symbols.RWuse          |
                    SP_Symbols.RWwhen         |
                    SP_Symbols.RWwhile        |
                    SP_Symbols.RWwith         |
                    SP_Symbols.RWxor          |
                    -- Ada2005 reserved words
                    SP_Symbols.RWinterface    |
                    SP_Symbols.RWoverriding   |
                    SP_Symbols.RWsynchronized =>
                     Token_Kind := Reserved_Word;
                  when others =>
                     Token_Kind := Other_Token;
               end case;
               return Token_Kind;
            end Type_Of_Token;

         begin
            Token_Type1 := Type_Of_Token (Token => Token1);
            Token_Type2 := Type_Of_Token (Token => Token2);
            case Token_Type1 is
               when Delimiter =>
                  case Token_Type2 is
                     when Delimiter =>
                        Result := True;
                     when others =>
                        Result := False;
                  end case;
               when Number =>
                  case Token_Type2 is
                     when Number =>
                        Result := True;
                     when others =>
                        Result := False;
                  end case;
               when Chars =>
                  case Token_Type2 is
                     when Chars =>
                        Result := True;
                     when others =>
                        Result := False;
                  end case;
               when Id =>
                  case Token_Type2 is
                     when Id =>
                        Result := True;
                     when Reserved_Word =>
                        Result := True;
                     when others =>
                        Result := False;
                  end case;
               when Reserved_Word =>
                  case Token_Type2 is
                     when Id =>
                        Result := True;
                     when Reserved_Word =>
                        Result := True;
                     when others =>
                        Result := False;
                  end case;
               when others =>
                  Result := False;
            end case;
            return Result;
         end Similar_Tokens;

end SparkLex;
