-------------------------------------------------------------------------------
-- (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 (Dictionary)
function Get_Binary_Operator_Type_Local
  (Name           : SP_Symbols.SP_Symbol;
   The_Left_Type  : RawDict.Type_Info_Ref;
   The_Right_Type : RawDict.Type_Info_Ref)
  return           RawDict.Type_Info_Ref is
   type Kind_Of_Result is (Left_Defined, Right_Defined, Never_Defined, Match);
   Result : RawDict.Type_Info_Ref;

   --------------------------------------------------------------------------------
   -- Logical operators (and, or, xor) are _defined_ (but not necessarily
   -- visible) according to the following table.
   --------------------------------------------------------------------------------
   function Get_Logical_Operators_Type (The_Left_Type, The_Right_Type : RawDict.Type_Info_Ref) return RawDict.Type_Info_Ref
   --# global in Dict;
   is
      type Kind_Of_Type is (BO_Boolean_Type,
                            BO_Boolean_Array_Type,
                            BO_Modular_Type,
                            BO_Universal_Integer_Type,
                            BO_Unknown_Type,
                            BO_Other_Type);
      type Array_Right_Type is array (Kind_Of_Type) of Kind_Of_Result;
      type Array_Left_Type is array (Kind_Of_Type) of Array_Right_Type;
      pragma Style_Checks (Off);
      The_Table : constant Array_Left_Type := Array_Left_Type'
        -- Right operand         Boolean, Boolean Array,       Modular, Universal Int,       Unknown,         Other    -- Left operand
        (Array_Right_Type'(        Match, Never_Defined, Never_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Boolean
         Array_Right_Type'(Never_Defined,         Match, Never_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Boolean Array
         Array_Right_Type'(Never_Defined, Never_Defined,         Match,  Left_Defined,  Left_Defined, Never_Defined),  -- Modular
         Array_Right_Type'(Never_Defined, Never_Defined, Right_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Universal Int
         Array_Right_Type'(Right_Defined, Right_Defined, Right_Defined, Right_Defined,  Left_Defined, Never_Defined),  -- Unknown
         Array_Right_Type'(Never_Defined, Never_Defined, Never_Defined, Never_Defined, Never_Defined, Never_Defined)); -- Other
      pragma Style_Checks (On);
      Result : RawDict.Type_Info_Ref;

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

      function Get_Kind_Of_Type (Type_Mark : RawDict.Type_Info_Ref) return Kind_Of_Type
      --# global in Dict;
      is
         Result : Kind_Of_Type;
      begin
         if Type_Is_Boolean (Type_Mark => Type_Mark) then
            Result := BO_Boolean_Type;
         elsif Type_Is_Boolean_Array (Type_Mark => Type_Mark) then
            Result := BO_Boolean_Array_Type;
         elsif Type_Is_Modular (Type_Mark => Type_Mark) then
            Result := BO_Modular_Type;
         elsif Type_Mark = Get_Universal_Integer_Type then
            Result := BO_Universal_Integer_Type;
         elsif Type_Mark = Get_Unknown_Type_Mark then
            Result := BO_Unknown_Type;
         else
            Result := BO_Other_Type;
         end if;
         return Result;
      end Get_Kind_Of_Type;

   begin -- Get_Logical_Operators_Type
      case The_Table (Get_Kind_Of_Type (Type_Mark => The_Left_Type)) (Get_Kind_Of_Type (Type_Mark => The_Right_Type)) is
         when Left_Defined =>
            Result := The_Left_Type;
         when Right_Defined =>
            Result := The_Right_Type;
         when Never_Defined =>
            Result := RawDict.Null_Type_Info_Ref;
         when Match =>
            if The_Left_Type = The_Right_Type then
               Result := The_Left_Type;
            else
               Result := RawDict.Null_Type_Info_Ref;
            end if;
      end case;
      return Result;
   end Get_Logical_Operators_Type;

   --------------------------------------------------------------------------------
   -- Covers = and /=
   --
   -- The EqualityDefined attribute is set when the type is added to the
   -- dictionary.
   --------------------------------------------------------------------------------
   function Get_Equality_Operators_Type (The_Left_Type, The_Right_Type : RawDict.Type_Info_Ref) return RawDict.Type_Info_Ref
   --# global in Dict;
   is
      Result : RawDict.Type_Info_Ref;
   begin
      if The_Left_Type = The_Right_Type or else The_Right_Type = Get_Unknown_Type_Mark then
         if RawDict.Get_Type_Equality_Defined (Type_Mark => The_Left_Type) then
            Result := Get_Predefined_Boolean_Type;
         else
            Result := RawDict.Null_Type_Info_Ref;
         end if;
      elsif The_Left_Type = Get_Unknown_Type_Mark then
         if RawDict.Get_Type_Equality_Defined (Type_Mark => The_Right_Type) then
            Result := Get_Predefined_Boolean_Type;
         else
            Result := RawDict.Null_Type_Info_Ref;
         end if;
      else
         Result := RawDict.Null_Type_Info_Ref;
      end if;
      return Result;
   end Get_Equality_Operators_Type;

   --------------------------------------------------------------------------------
   -- Relational ordering operators (<= >= < >) are _defined_ (but not necessarily
   -- visible) according to the following table.
   --   Scalar = Integer, Modular, Enumeration, Real (but not Boolean)
   --------------------------------------------------------------------------------
   function Get_Relational_Operators_Type (The_Left_Type, The_Right_Type : RawDict.Type_Info_Ref) return RawDict.Type_Info_Ref
   --# global in CommandLineData.Content;
   --#        in Dict;
   is
      type Kind_Of_Type is (BO_Scalar_Type,
                            BO_Boolean_Type,
                            BO_Predefined_String,
                            BO_Constrained_String,
                            BO_Time_Type,
                            BO_Unknown_Type,
                            BO_Other_Type);
      type Array_Right_Type is array (Kind_Of_Type) of Kind_Of_Result;
      type Array_Left_Type is array (Kind_Of_Type) of Array_Right_Type;
      pragma Style_Checks (Off);
      The_Table : constant Array_Left_Type := Array_Left_Type'
        -- Right operand          Scalar,       Boolean, Predefined Str, Constrained Str,          Time,       Unknown,         Other    -- Left operand
        (Array_Right_Type'(        Match, Never_Defined,  Never_Defined,   Never_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Scalar
         Array_Right_Type'(Never_Defined, Never_Defined,  Never_Defined,   Never_Defined, Never_Defined, Never_Defined, Never_Defined),  -- Boolean
         Array_Right_Type'(Never_Defined, Never_Defined,          Match,   Never_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Predefined Str
         Array_Right_Type'(Never_Defined, Never_Defined,  Never_Defined,           Match, Never_Defined,  Left_Defined, Never_Defined),  -- Constrained Str
         Array_Right_Type'(Never_Defined, Never_Defined,  Never_Defined,   Never_Defined,         Match,  Left_Defined, Never_Defined),  -- Time
         Array_Right_Type'(Right_Defined, Never_Defined,  Right_Defined,   Right_Defined, Right_Defined,  Left_Defined, Never_Defined),  -- Unknown
         Array_Right_Type'(Never_Defined, Never_Defined,  Never_Defined,   Never_Defined, Never_Defined, Never_Defined, Never_Defined)); -- Other
      pragma Style_Checks (On);
      Result : RawDict.Type_Info_Ref;

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

      function Get_Kind_Of_Type (Type_Mark : RawDict.Type_Info_Ref) return Kind_Of_Type
      --# global in CommandLineData.Content;
      --#        in Dict;
      is
         Result : Kind_Of_Type;
      begin
         if Type_Is_Boolean (Type_Mark => Type_Mark) then
            Result := BO_Boolean_Type;
         elsif Type_Is_Scalar (Type_Mark => Type_Mark) then
            Result := BO_Scalar_Type;
         elsif Type_Is_Array (Type_Mark => Type_Mark) then
            if Is_Type (Type_Mark => Type_Mark) then
               if Type_Mark = Get_Predefined_String_Type then
                  Result := BO_Predefined_String;
               else
                  Result := BO_Constrained_String;
               end if;
            else
               if Get_Root_Type (Type_Mark => Type_Mark) = Get_Predefined_String_Type then
                  Result := BO_Predefined_String;
               else
                  Result := BO_Constrained_String;
               end if;
            end if;
         elsif CommandLineData.Ravenscar_Selected and then Is_Predefined_Time_Type (Type_Mark => Type_Mark) then
            Result := BO_Time_Type;
         elsif Type_Mark = Get_Unknown_Type_Mark then
            Result := BO_Unknown_Type;
         else
            Result := BO_Other_Type;
         end if;
         return Result;
      end Get_Kind_Of_Type;

   begin -- Get_Relational_Operators_Type
      case The_Table (Get_Kind_Of_Type (Type_Mark => The_Left_Type)) (Get_Kind_Of_Type (Type_Mark => The_Right_Type)) is
         when Left_Defined =>
            Result := Get_Predefined_Boolean_Type;
         when Right_Defined =>
            Result := Get_Predefined_Boolean_Type;
         when Never_Defined =>
            Result := RawDict.Null_Type_Info_Ref;
         when Match =>
            if The_Left_Type = The_Right_Type then
               Result := Get_Predefined_Boolean_Type;
            else
               Result := RawDict.Null_Type_Info_Ref;
            end if;
      end case;
      return Result;
   end Get_Relational_Operators_Type;

   --------------------------------------------------------------------------------
   -- Binary Adding operators + and - (but not &) are _defined_
   -- (but not necessarily visible) according
   -- to the following table.
   --
   -- Result codes are
   --   Y  = Always defined
   --   N  = Never defined
   --
   -- For the types Time and Time_Span in package Ada.Real_Time, the adding
   -- operators are defined as follows, with T = Time and TS = Time_Span:
   --
   -- Op "+" Right  T   TS  U   O         Op "-" Right  T   TS  U   O
   -- Left                                Left
   --  T            N   Y   Y   N          T            Y   Y   Y   N
   --  TS           Y   Y   Y   N          TS           N   Y   Y   N
   --  U            Y   Y   Y   N          U            Y   Y   Y   N
   --  O            N   N   N   N          O            N   N   N   N
   --------------------------------------------------------------------------------
   function Get_Adding_Operators_Type
     (Name                          : SP_Symbols.SP_Symbol;
      The_Left_Type, The_Right_Type : RawDict.Type_Info_Ref)
     return                          RawDict.Type_Info_Ref
   --# global in CommandLineData.Content;
   --#        in Dict;
   is
      type Kind_Of_Type is (BO_Integer_Type,
                            BO_Real_Type,
                            BO_Universal_Integer_Type,
                            BO_Universal_Real_Type,
                            BO_Time_Type,
                            BO_Time_Span_Type,
                            BO_Unknown_Type,
                            BO_Other_Type);
      type Array_Right_Type is array (Kind_Of_Type) of Kind_Of_Result;
      type Array_Left_Type is array (Kind_Of_Type) of Array_Right_Type;
      pragma Style_Checks (Off);
      The_Table : constant Array_Left_Type := Array_Left_Type'
        -- Right operand         Integer,          Real, Universal Int, Universal Real,          Time,     Time Span,       Unknown,         Other    -- Left operand
        (Array_Right_Type'(        Match, Never_Defined,  Left_Defined,  Never_Defined, Never_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Integer
         Array_Right_Type'(Never_Defined,         Match, Never_Defined,   Left_Defined, Never_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Real
         Array_Right_Type'(Right_Defined, Never_Defined,         Match,  Never_Defined, Never_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Universal Int
         Array_Right_Type'(Never_Defined, Right_Defined, Never_Defined,          Match, Never_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Universal Real
         Array_Right_Type'(Never_Defined, Never_Defined, Never_Defined,  Never_Defined,         Match,  Left_Defined,  Left_Defined, Never_Defined),  -- Time
         Array_Right_Type'(Never_Defined, Never_Defined, Never_Defined,  Never_Defined,         Match,  Left_Defined,  Left_Defined, Never_Defined),  -- Time Span
         Array_Right_Type'(Right_Defined, Right_Defined, Right_Defined,  Right_Defined, Right_Defined, Right_Defined,  Left_Defined, Never_Defined),  -- Unknown
         Array_Right_Type'(Never_Defined, Never_Defined, Never_Defined,  Never_Defined, Never_Defined, Never_Defined, Never_Defined, Never_Defined)); -- Other
      pragma Style_Checks (On);
      Result : RawDict.Type_Info_Ref;

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

      function Get_Kind_Of_Type (Type_Mark : RawDict.Type_Info_Ref) return Kind_Of_Type
      --# global in CommandLineData.Content;
      --#        in Dict;
      is
         Result : Kind_Of_Type;
      begin
         if Type_Mark = Get_Universal_Integer_Type then
            Result := BO_Universal_Integer_Type;
         elsif Type_Mark = Get_Universal_Real_Type or else Type_Mark = Get_Universal_Fixed_Type then
            Result := BO_Universal_Real_Type;
         elsif Type_Is_Integer (Type_Mark => Type_Mark) or else Type_Is_Modular (Type_Mark => Type_Mark) then
            Result := BO_Integer_Type;
         elsif Type_Is_Real (Type_Mark => Type_Mark) then
            Result := BO_Real_Type;
         elsif CommandLineData.Ravenscar_Selected and then Is_Predefined_Time_Type (Type_Mark => Type_Mark) then
            if Type_Mark = Get_Predefined_Time_Type then
               Result := BO_Time_Type;
            else
               Result := BO_Time_Span_Type;
            end if;
         elsif Type_Mark = Get_Unknown_Type_Mark then
            Result := BO_Unknown_Type;
         else
            Result := BO_Other_Type;
         end if;
         return Result;
      end Get_Kind_Of_Type;

   begin -- Get_Adding_Operators_Type
      case The_Table (Get_Kind_Of_Type (Type_Mark => The_Left_Type)) (Get_Kind_Of_Type (Type_Mark => The_Right_Type)) is
         when Left_Defined =>
            Result := The_Left_Type;
         when Right_Defined =>
            Result := The_Right_Type;
         when Never_Defined =>
            Result := RawDict.Null_Type_Info_Ref;
         when Match =>
            if CommandLineData.Ravenscar_Selected and then Get_Kind_Of_Type (Type_Mark => The_Right_Type) = BO_Time_Type then
               if Name = SP_Symbols.plus and then The_Left_Type = Get_Predefined_Time_Span_Type then
                  Result := Get_Predefined_Time_Type;
               elsif Name = SP_Symbols.minus and then The_Left_Type = Get_Predefined_Time_Type then
                  Result := Get_Predefined_Time_Span_Type;
               else
                  Result := RawDict.Null_Type_Info_Ref;
               end if;
            elsif The_Left_Type = The_Right_Type then
               Result := The_Left_Type;
            else
               Result := RawDict.Null_Type_Info_Ref;
            end if;
      end case;
      return Result;
   end Get_Adding_Operators_Type;

   --------------------------------------------------------------------------------
   -- Modular types are Numeric types, so no change here to accomodate modular
   -- types.
   -- For the Time_Span type, multiplication is defined between TS and Integer.
   --------------------------------------------------------------------------------
   function Get_Multiplication_Operator_Type
     (The_Left_Type, The_Right_Type : RawDict.Type_Info_Ref)
     return                          RawDict.Type_Info_Ref
   --# global in CommandLineData.Content;
   --#        in Dict;
   is
      type Kind_Of_Type is (BO_Integer_Type,
                            BO_Fixed_Point_Type,
                            BO_Floating_Point_Type,
                            BO_Universal_Integer_Type,
                            BO_Universal_Fixed_Point_Type,
                            BO_Universal_Floating_Point_Type,
                            BO_Time_Span_Type,
                            BO_Unknown_Type,
                            BO_Other_Type);
      type Array_Right_Type is array (Kind_Of_Type) of Kind_Of_Result;
      type Array_Left_Type is array (Kind_Of_Type) of Array_Right_Type;
      pragma Style_Checks (Off);
      The_Table : constant Array_Left_Type := Array_Left_Type'
        -- Right operand         Integer,         Fixed,         Float, Universal Int, Universal Fixed, Universal Float,     Time Span,       Unknown,         Other    -- Left operand
        (Array_Right_Type'(        Match, Right_Defined, Never_Defined,  Left_Defined,   Right_Defined,   Never_Defined, Right_Defined,  Left_Defined, Never_Defined),  -- Integer
         Array_Right_Type'( Left_Defined,         Match, Never_Defined,  Left_Defined,   Right_Defined,   Never_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Fixed
         Array_Right_Type'(Never_Defined, Never_Defined,         Match, Never_Defined,   Never_Defined,    Left_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Float
         Array_Right_Type'(Right_Defined, Right_Defined, Never_Defined,  Left_Defined,   Right_Defined,   Right_Defined, Right_Defined,  Left_Defined, Never_Defined),  -- Universal Int
         Array_Right_Type'( Left_Defined,  Left_Defined, Never_Defined,  Left_Defined,    Left_Defined,   Never_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Universal Fixed
         Array_Right_Type'(Never_Defined, Never_Defined, Right_Defined,  Left_Defined,   Never_Defined,    Left_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Universal Float
         Array_Right_Type'( Left_Defined, Never_Defined, Never_Defined,  Left_Defined,   Never_Defined,   Never_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Time Span
         Array_Right_Type'(Right_Defined, Right_Defined, Right_Defined, Right_Defined,   Right_Defined,   Right_Defined, Right_Defined,  Left_Defined, Never_Defined),  -- Unknown
         Array_Right_Type'(Never_Defined, Never_Defined, Never_Defined, Never_Defined,   Never_Defined,   Never_Defined, Never_Defined, Never_Defined, Never_Defined)); -- Other
      pragma Style_Checks (On);
      Result : RawDict.Type_Info_Ref;

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

      function Get_Kind_Of_Type (Type_Mark : RawDict.Type_Info_Ref) return Kind_Of_Type
      --# global in CommandLineData.Content;
      --#        in Dict;
      is
         Result : Kind_Of_Type;
      begin
         if Type_Mark = Get_Universal_Integer_Type then
            Result := BO_Universal_Integer_Type;
         elsif Type_Mark = Get_Universal_Fixed_Type then
            Result := BO_Universal_Fixed_Point_Type;
         elsif Type_Mark = Get_Universal_Real_Type then
            Result := BO_Universal_Floating_Point_Type;
         elsif Type_Is_Integer (Type_Mark => Type_Mark) or else Type_Is_Modular (Type_Mark => Type_Mark) then
            Result := BO_Integer_Type;
         elsif Type_Is_Fixed_Point (Type_Mark => Type_Mark) then
            Result := BO_Fixed_Point_Type;
         elsif Type_Is_Floating_Point (Type_Mark => Type_Mark) then
            Result := BO_Floating_Point_Type;
         elsif CommandLineData.Ravenscar_Selected and then Is_Predefined_Time_Type (Type_Mark => Type_Mark) then
            if Type_Mark = Get_Predefined_Time_Type then
               Result := BO_Unknown_Type;
            else
               Result := BO_Time_Span_Type;
            end if;
         elsif Type_Mark = Get_Unknown_Type_Mark then
            Result := BO_Unknown_Type;
         else
            Result := BO_Other_Type;
         end if;
         return Result;
      end Get_Kind_Of_Type;

   begin -- Get_Multiplication_Operator_Type
      case The_Table (Get_Kind_Of_Type (Type_Mark => The_Left_Type)) (Get_Kind_Of_Type (Type_Mark => The_Right_Type)) is
         when Left_Defined =>
            Result := The_Left_Type;
         when Right_Defined =>
            Result := The_Right_Type;
         when Never_Defined =>
            Result := RawDict.Null_Type_Info_Ref;
         when Match =>
            if Get_Kind_Of_Type (Type_Mark => The_Left_Type) = BO_Fixed_Point_Type
              and then Get_Kind_Of_Type (Type_Mark => The_Left_Type) = BO_Fixed_Point_Type then
               Result := Get_Universal_Fixed_Type;
            elsif The_Left_Type = The_Right_Type then
               Result := The_Left_Type;
            else
               Result := RawDict.Null_Type_Info_Ref;
            end if;
      end case;
      return Result;
   end Get_Multiplication_Operator_Type;

   --------------------------------------------------------------------------------
   -- / for signed integer, real, and modular types are defined for
   --    1) Any matching pair of numeric types
   --    2) Any pair of fixed point types
   --    3) Any fixed point type on the left with Standard.Integer on the right
   --    4) Universal real on the left, and Universal integer on the right
   --    5) Any type with an unknown type (to prevent needless propagation of errors)
   -- For type Time_Span, "/" is defined for TS/TS and TS/Integer.
   --------------------------------------------------------------------------------
   function Get_Division_Operator_Type (The_Left_Type, The_Right_Type : RawDict.Type_Info_Ref) return RawDict.Type_Info_Ref
   --# global in CommandLineData.Content;
   --#        in Dict;
   is
      type Kind_Of_Type is (BO_Integer_Type,
                            BO_Fixed_Point_Type,
                            BO_Floating_Point_Type,
                            BO_Universal_Integer_Type,
                            BO_Universal_Fixed_Point_Type,
                            BO_Universal_Floating_Point_Type,
                            BO_Time_Span_Type,
                            BO_Unknown_Type,
                            BO_Other_Type);
      type Array_Right_Type is array (Kind_Of_Type) of Kind_Of_Result;
      type Array_Left_Type is array (Kind_Of_Type) of Array_Right_Type;
      pragma Style_Checks (Off);
      The_Table : constant Array_Left_Type := Array_Left_Type'
        -- Right operand         Integer,         Fixed,         Float, Universal Int, Universal Fixed, Universal Float,     Time Span,       Unknown,         Other    -- Left operand
        (Array_Right_Type'(        Match, Never_Defined, Never_Defined,  Left_Defined,   Never_Defined,   Never_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Integer
         Array_Right_Type'( Left_Defined,         Match, Never_Defined,  Left_Defined,   Right_Defined,   Never_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Fixed
         Array_Right_Type'(Never_Defined, Never_Defined,         Match, Never_Defined,   Never_Defined,    Left_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Float
         Array_Right_Type'(Right_Defined, Never_Defined, Never_Defined,  Left_Defined,   Never_Defined,   Never_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Universal Int
         Array_Right_Type'( Left_Defined,  Left_Defined, Never_Defined,  Left_Defined,    Left_Defined,   Never_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Universal Fixed
         Array_Right_Type'(Never_Defined, Never_Defined, Right_Defined,  Left_Defined,   Never_Defined,    Left_Defined, Never_Defined,  Left_Defined, Never_Defined),  -- Universal Float
         Array_Right_Type'( Left_Defined, Never_Defined, Never_Defined,  Left_Defined,   Never_Defined,   Never_Defined,         Match,  Left_Defined, Never_Defined),  -- Time Span
         Array_Right_Type'(Right_Defined, Right_Defined, Right_Defined, Right_Defined,   Right_Defined,   Right_Defined, Right_Defined,  Left_Defined, Never_Defined),  -- Unknown
         Array_Right_Type'(Never_Defined, Never_Defined, Never_Defined, Never_Defined,   Never_Defined,   Never_Defined, Never_Defined, Never_Defined, Never_Defined)); -- Other
      pragma Style_Checks (On);
      Result : RawDict.Type_Info_Ref;

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

      function Get_Kind_Of_Type (Type_Mark : RawDict.Type_Info_Ref) return Kind_Of_Type
      --# global in CommandLineData.Content;
      --#        in Dict;
      is
         Result : Kind_Of_Type;
      begin
         if Type_Mark = Get_Universal_Integer_Type then
            Result := BO_Universal_Integer_Type;
         elsif Type_Mark = Get_Universal_Fixed_Type then
            Result := BO_Universal_Fixed_Point_Type;
         elsif Type_Mark = Get_Universal_Real_Type then
            Result := BO_Universal_Floating_Point_Type;
         elsif Type_Is_Integer (Type_Mark => Type_Mark) or else Type_Is_Modular (Type_Mark => Type_Mark) then
            Result := BO_Integer_Type;
         elsif Type_Is_Fixed_Point (Type_Mark => Type_Mark) then
            Result := BO_Fixed_Point_Type;
         elsif Type_Is_Floating_Point (Type_Mark => Type_Mark) then
            Result := BO_Floating_Point_Type;
         elsif CommandLineData.Ravenscar_Selected and then Is_Predefined_Time_Type (Type_Mark => Type_Mark) then
            if Type_Mark = Get_Predefined_Time_Type then
               Result := BO_Other_Type;
            else
               Result := BO_Time_Span_Type;
            end if;
         elsif Type_Mark = Get_Unknown_Type_Mark then
            Result := BO_Unknown_Type;
         else
            Result := BO_Other_Type;
         end if;
         return Result;
      end Get_Kind_Of_Type;

   begin -- Get_Division_Operator_Type
      case The_Table (Get_Kind_Of_Type (Type_Mark => The_Left_Type)) (Get_Kind_Of_Type (Type_Mark => The_Right_Type)) is
         when Left_Defined =>
            Result := The_Left_Type;
         when Right_Defined =>
            Result := The_Right_Type;
         when Never_Defined =>
            Result := RawDict.Null_Type_Info_Ref;
         when Match =>
            if Get_Kind_Of_Type (Type_Mark => The_Left_Type) = BO_Fixed_Point_Type
              and then Get_Kind_Of_Type (Type_Mark => The_Left_Type) = BO_Fixed_Point_Type then
               Result := Get_Universal_Fixed_Type;
            elsif CommandLineData.Ravenscar_Selected
              and then Get_Kind_Of_Type (Type_Mark => The_Left_Type) = BO_Time_Span_Type
              and then Get_Kind_Of_Type (Type_Mark => The_Right_Type) = BO_Time_Span_Type then
               Result := Get_Predefined_Integer_Type;
            elsif The_Left_Type = The_Right_Type then
               Result := The_Left_Type;
            else
               Result := RawDict.Null_Type_Info_Ref;
            end if;
      end case;
      return Result;
   end Get_Division_Operator_Type;

   --------------------------------------------------------------------------------
   -- mod and rem for signed integer and modular types is defined for
   --  1) Matching pairs of integer or modular types
   --  2) Any integer or modular type and an unknown type
   --------------------------------------------------------------------------------
   function Get_Integer_Division_Operators_Type
     (The_Left_Type, The_Right_Type : RawDict.Type_Info_Ref)
     return                          RawDict.Type_Info_Ref
   --# global in Dict;
   is
      type Kind_Of_Type is (BO_Integer_Type,
                            BO_Unknown_Type,
                            BO_Other_Type);
      type Array_Right_Type is array (Kind_Of_Type) of Kind_Of_Result;
      type Array_Left_Type is array (Kind_Of_Type) of Array_Right_Type;
      pragma Style_Checks (Off);
      The_Table : constant Array_Left_Type := Array_Left_Type'
        -- Right operand         Integer,       Unknown,         Other    -- Left operand
        (Array_Right_Type'(        Match,  Left_Defined, Never_Defined),  -- Integer
         Array_Right_Type'(Right_Defined,  Left_Defined, Never_Defined),  -- Unknown
         Array_Right_Type'(Never_Defined, Never_Defined, Never_Defined)); -- Other
      pragma Style_Checks (On);
      Result : RawDict.Type_Info_Ref;

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

      function Get_Kind_Of_Type (Type_Mark : RawDict.Type_Info_Ref) return Kind_Of_Type
      --# global in Dict;
      is
         Result : Kind_Of_Type;
      begin
         if Type_Is_Integer (Type_Mark => Type_Mark) or else Type_Is_Modular (Type_Mark => Type_Mark) then
            Result := BO_Integer_Type;
         elsif Type_Mark = Get_Unknown_Type_Mark then
            Result := BO_Unknown_Type;
         else
            Result := BO_Other_Type;
         end if;
         return Result;
      end Get_Kind_Of_Type;

   begin -- Get_Integer_Division_Operators_Type
      case The_Table (Get_Kind_Of_Type (Type_Mark => The_Left_Type)) (Get_Kind_Of_Type (Type_Mark => The_Right_Type)) is
         when Left_Defined =>
            Result := The_Left_Type;
         when Right_Defined =>
            Result := The_Right_Type;
         when Never_Defined =>
            Result := RawDict.Null_Type_Info_Ref;
         when Match =>
            if The_Left_Type = The_Right_Type then
               Result := The_Left_Type;
            else
               Result := RawDict.Null_Type_Info_Ref;
            end if;
      end case;
      return Result;
   end Get_Integer_Division_Operators_Type;

   --------------------------------------------------------------------------------
   -- ** is defined (but not necessairily visible for)
   --   1) Any Integer type ** Standard.Integer
   --   2) Any Modular type ** Standard.Integer
   --   3) Any Floating point type ** Standard.Integer
   --   4) Any Unknown type ** Standard.Integer
   --   5) as 1) thru 4) with Unknown on the RHS
   --------------------------------------------------------------------------------
   function Get_Exponentiation_Operator_Type
     (The_Left_Type, The_Right_Type : RawDict.Type_Info_Ref)
     return                          RawDict.Type_Info_Ref
   --# global in Dict;
   is
      type Kind_Of_Type is (BO_Integer_Type,
                            BO_Floating_Point_Type,
                            BO_Predefined_Integer_Type,
                            BO_Universal_Integer_Type,
                            BO_Universal_Floating_Point_Type,
                            BO_Unknown_Type,
                            BO_Other_Type);
      type Array_Right_Type is array (Kind_Of_Type) of Kind_Of_Result;
      type Array_Left_Type is array (Kind_Of_Type) of Array_Right_Type;
      pragma Style_Checks (Off);
      The_Table : constant Array_Left_Type := Array_Left_Type'
        -- Right operand         Integer,         Float, Predefined Int, Universal Int, Universal Float,       Unknown,         Other    -- Left operand
        (Array_Right_Type'(Never_Defined, Never_Defined,   Left_Defined,  Left_Defined,   Never_Defined,  Left_Defined, Never_Defined),  -- Integer
         Array_Right_Type'(Never_Defined, Never_Defined,   Left_Defined,  Left_Defined,   Never_Defined,  Left_Defined, Never_Defined),  -- Float
         Array_Right_Type'(Never_Defined, Never_Defined,   Left_Defined,  Left_Defined,   Never_Defined,  Left_Defined, Never_Defined),  -- Predefined Int
         Array_Right_Type'(Never_Defined, Never_Defined,   Left_Defined,  Left_Defined,   Never_Defined,  Left_Defined, Never_Defined),  -- Universal Int
         Array_Right_Type'(Never_Defined, Never_Defined,   Left_Defined,  Left_Defined,   Never_Defined,  Left_Defined, Never_Defined),  -- Universal Float
         Array_Right_Type'(Never_Defined, Never_Defined,  Right_Defined, Right_Defined,   Never_Defined,  Left_Defined, Never_Defined),  -- Unknown
         Array_Right_Type'(Never_Defined, Never_Defined,  Never_Defined, Never_Defined,   Never_Defined, Never_Defined, Never_Defined)); -- Other
      pragma Style_Checks (On);
      Result : RawDict.Type_Info_Ref;

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

      function Get_Kind_Of_Type (Type_Mark : RawDict.Type_Info_Ref) return Kind_Of_Type
      --# global in Dict;
      is
         Result : Kind_Of_Type;
      begin
         if Type_Mark = Get_Universal_Integer_Type then
            Result := BO_Universal_Integer_Type;
         elsif Type_Mark = Get_Universal_Real_Type then
            Result := BO_Universal_Floating_Point_Type;
         elsif Type_Mark = Get_Predefined_Integer_Type then
            Result := BO_Predefined_Integer_Type;
         elsif Type_Is_Integer (Type_Mark => Type_Mark) or else Type_Is_Modular (Type_Mark => Type_Mark) then
            Result := BO_Integer_Type;
         elsif Type_Is_Floating_Point (Type_Mark => Type_Mark) then
            Result := BO_Floating_Point_Type;
         elsif Type_Mark = Get_Unknown_Type_Mark then
            Result := BO_Unknown_Type;
         else
            Result := BO_Other_Type;
         end if;
         return Result;
      end Get_Kind_Of_Type;

   begin -- Get_Exponentiation_Operator_Type
      case The_Table (Get_Kind_Of_Type (Type_Mark => The_Left_Type)) (Get_Kind_Of_Type (Type_Mark => The_Right_Type)) is
         when Left_Defined =>
            Result := The_Left_Type;
         when Right_Defined =>
            Result := The_Right_Type;
         when Never_Defined =>
            Result := RawDict.Null_Type_Info_Ref;
         when Match =>
            if The_Left_Type = The_Right_Type then
               Result := The_Left_Type;
            else
               Result := RawDict.Null_Type_Info_Ref;
            end if;
      end case;
      return Result;
   end Get_Exponentiation_Operator_Type;

begin -- Get_Binary_Operator_Type_Local
   case Name is
      when SP_Symbols.RWand | SP_Symbols.RWor | SP_Symbols.RWxor =>
         Result := Get_Logical_Operators_Type (The_Left_Type  => The_Left_Type,
                                               The_Right_Type => The_Right_Type);
      when SP_Symbols.equals | SP_Symbols.not_equal =>
         Result := Get_Equality_Operators_Type (The_Left_Type  => The_Left_Type,
                                                The_Right_Type => The_Right_Type);
      when SP_Symbols.less_than | SP_Symbols.less_or_equal | SP_Symbols.greater_than | SP_Symbols.greater_or_equal =>
         Result := Get_Relational_Operators_Type (The_Left_Type  => The_Left_Type,
                                                  The_Right_Type => The_Right_Type);
      when SP_Symbols.plus | SP_Symbols.minus =>
         Result := Get_Adding_Operators_Type (Name           => Name,
                                              The_Left_Type  => The_Left_Type,
                                              The_Right_Type => The_Right_Type);
      when SP_Symbols.multiply =>
         Result := Get_Multiplication_Operator_Type (The_Left_Type  => The_Left_Type,
                                                     The_Right_Type => The_Right_Type);
      when SP_Symbols.divide =>
         Result := Get_Division_Operator_Type (The_Left_Type  => The_Left_Type,
                                               The_Right_Type => The_Right_Type);
      when SP_Symbols.RWmod | SP_Symbols.RWrem =>
         Result := Get_Integer_Division_Operators_Type (The_Left_Type  => The_Left_Type,
                                                        The_Right_Type => The_Right_Type);
      when SP_Symbols.double_star =>
         Result := Get_Exponentiation_Operator_Type (The_Left_Type  => The_Left_Type,
                                                     The_Right_Type => The_Right_Type);
      when others =>
         Result := RawDict.Null_Type_Info_Ref;
   end case;
   return Result;
end Get_Binary_Operator_Type_Local;
