--  Nodes recognizer for ieee.std_logic_1164.
--  Copyright (C) 2002, 2003, 2004, 2005 Tristan Gingold
--
--  GHDL is free software; you can redistribute it and/or modify it under
--  the terms of the GNU General Public License as published by the Free
--  Software Foundation; either version 2, or (at your option) any later
--  version.
--
--  GHDL 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
--  along with GHDL; see the file COPYING.  If not, write to the Free
--  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
--  02111-1307, USA.
with Types; use Types;
with Name_Table;
with Std_Names; use Std_Names;
with Errorout; use Errorout;

package body Ieee.Std_Logic_1164 is
   function Is_Scalar_Parameter (Inter : Iir) return Boolean is
   begin
      return Get_Base_Type (Get_Type (Inter)) = Std_Ulogic_Type;
   end Is_Scalar_Parameter;

   function Is_Vector_Parameter (Inter : Iir) return Boolean
   is
      Base_Type : constant Iir := Get_Base_Type (Get_Type (Inter));
   begin
      return Base_Type = Std_Ulogic_Vector_Type
        or Base_Type = Std_Logic_Vector_Type;
   end Is_Vector_Parameter;

   --  Return True iff the profile of FUNC is: (l, r : std_ulogic)
   function Is_Scalar_Scalar_Function (Func : Iir) return Boolean
   is
      Inter : constant Iir := Get_Interface_Declaration_Chain (Func);
      Inter2 : Iir;
   begin
      if Get_Implicit_Definition (Func) /= Iir_Predefined_None then
         return False;
      end if;
      if Inter = Null_Iir or else not Is_Scalar_Parameter (Inter) then
         return False;
      end if;
      Inter2 := Get_Chain (Inter);
      if Inter2 =  Null_Iir or else not Is_Scalar_Parameter (Inter2) then
         return False;
      end if;
      if Get_Chain (Inter2) /= Null_Iir then
         return False;
      end if;

      return True;
   end Is_Scalar_Scalar_Function;

   --  Return True iff the profile of FUNC is: (l : std_ulogic)
   function Is_Scalar_Function (Func : Iir) return Boolean
   is
      Inter : constant Iir := Get_Interface_Declaration_Chain (Func);
   begin
      if Get_Implicit_Definition (Func) /= Iir_Predefined_None then
         return False;
      end if;
      if Inter = Null_Iir or else not Is_Scalar_Parameter (Inter) then
         return False;
      end if;
      if Get_Chain (Inter) /= Null_Iir then
         return False;
      end if;

      return True;
   end Is_Scalar_Function;

   --  Return True iff the profile of FUNC is: (l, r : std_[u]logic_vector)
   function Is_Vector_Vector_Function (Func : Iir) return Boolean
   is
      Inter : constant Iir := Get_Interface_Declaration_Chain (Func);
      Inter2 : Iir;
   begin
      if Get_Implicit_Definition (Func) /= Iir_Predefined_None then
         return False;
      end if;
      if Inter = Null_Iir or else not Is_Vector_Parameter (Inter) then
         return False;
      end if;
      Inter2 := Get_Chain (Inter);
      if Inter2 =  Null_Iir or else not Is_Vector_Parameter (Inter2) then
         return False;
      end if;
      if Get_Chain (Inter2) /= Null_Iir then
         return False;
      end if;

      return True;
   end Is_Vector_Vector_Function;

   --  Return True iff the profile of FUNC is: (l : std_[u]logic_vector)
   function Is_Vector_Function (Func : Iir) return Boolean
   is
      Inter : constant Iir := Get_Interface_Declaration_Chain (Func);
   begin
      if Get_Implicit_Definition (Func) /= Iir_Predefined_None then
         return False;
      end if;
      if Inter = Null_Iir or else not Is_Vector_Parameter (Inter) then
         return False;
      end if;
      if Get_Chain (Inter) /= Null_Iir then
         return False;
      end if;

      return True;
   end Is_Vector_Function;

   procedure Extract_Declarations (Pkg : Iir_Package_Declaration)
   is
      Error : exception;

      Decl : Iir;
      Def : Iir;
   begin
      Std_Logic_1164_Pkg := Pkg;

      Decl := Get_Declaration_Chain (Pkg);

      --  Skip a potential copyright constant.
      Decl := Skip_Copyright_Notice (Decl);

      --  The first declaration should be type std_ulogic.
      if Decl = Null_Iir
        or else Get_Kind (Decl) /= Iir_Kind_Type_Declaration
        or else Get_Identifier (Decl) /= Name_Std_Ulogic
      then
         raise Error;
      end if;

      Def := Get_Type_Definition (Decl);
      if Get_Kind (Def) /= Iir_Kind_Enumeration_Type_Definition then
         raise Error;
      end if;
      Std_Ulogic_Type := Def;

      --  Get node of some literals.
      declare
         use Name_Table;
         Lit_List : constant Iir_Flist := Get_Enumeration_Literal_List (Def);
      begin
         if Get_Nbr_Elements (Lit_List) /= 9 then
            raise Error;
         end if;
         Std_Ulogic_0 := Get_Nth_Element (Lit_List, 2);
         Std_Ulogic_1 := Get_Nth_Element (Lit_List, 3);
         if Get_Identifier (Std_Ulogic_0) /= Get_Identifier ('0')
           or else Get_Identifier (Std_Ulogic_1) /= Get_Identifier ('1')
         then
            raise Error;
         end if;
      end;

      --  The second declaration should be std_ulogic_vector.
      Decl := Get_Chain (Decl);
      Decl := Skip_Implicit (Decl);
      if Decl = Null_Iir
        or else Get_Kind (Decl) /= Iir_Kind_Type_Declaration
        or else Get_Identifier (Decl) /= Name_Std_Ulogic_Vector
      then
         raise Error;
      end if;
      Def := Get_Type_Definition (Decl);
      if Get_Kind (Def) /= Iir_Kind_Array_Type_Definition then
         raise Error;
      end if;
      Std_Ulogic_Vector_Type := Def;

      --  The third declaration should be resolved.
      Decl := Get_Chain (Decl);
      Decl := Skip_Implicit (Decl);
      if Decl = Null_Iir
        or else Get_Kind (Decl) /= Iir_Kind_Function_Declaration
      then
         --  FIXME: check name ?
         raise Error;
      end if;
      Resolved := Decl;

      --  The fourth declaration should be std_logic.
      Decl := Get_Chain (Decl);
      Decl := Skip_Implicit (Decl);
      if Decl = Null_Iir
        or else Get_Kind (Decl) /= Iir_Kind_Subtype_Declaration
        or else Get_Identifier (Decl) /= Name_Std_Logic
      then
         raise Error;
      end if;
      Def := Get_Type (Decl);
      if Get_Kind (Def) /= Iir_Kind_Enumeration_Subtype_Definition then
         raise Error;
      end if;
      Std_Logic_Type := Def;

      --  The fifth declaration should be std_logic_vector.
      Decl := Get_Chain (Decl);
      Decl := Skip_Implicit (Decl);
      if Decl = Null_Iir
        or else (Get_Kind (Decl) /= Iir_Kind_Type_Declaration
                   and then Get_Kind (Decl) /= Iir_Kind_Subtype_Declaration)
        or else Get_Identifier (Decl) /= Name_Std_Logic_Vector
      then
         raise Error;
      end if;
      Def := Get_Type (Decl);
--      if Get_Kind (Def) /= Iir_Kind_Array_Type_Definition then
--         raise Error;
--      end if;
      Std_Logic_Vector_Type := Def;

      --  Skip any declarations but functions.
      loop
         Decl := Get_Chain (Decl);
         exit when Decl = Null_Iir;

         if Get_Kind (Decl) = Iir_Kind_Function_Declaration then
            if Get_Identifier (Decl) = Name_Rising_Edge then
               Rising_Edge := Decl;
            elsif Get_Identifier (Decl) = Name_Falling_Edge then
               Falling_Edge := Decl;
            elsif Is_Scalar_Scalar_Function (Decl) then
               declare
                  Predefined : Iir_Predefined_Functions;
               begin
                  case Get_Identifier (Decl) is
                     when Name_And =>
                        Predefined := Iir_Predefined_Ieee_1164_Scalar_And;
                     when Name_Nand =>
                        Predefined := Iir_Predefined_Ieee_1164_Scalar_Nand;
                     when Name_Or =>
                        Predefined := Iir_Predefined_Ieee_1164_Scalar_Or;
                     when Name_Nor =>
                        Predefined := Iir_Predefined_Ieee_1164_Scalar_Nor;
                     when Name_Xor =>
                        Predefined := Iir_Predefined_Ieee_1164_Scalar_Xor;
                     when Name_Xnor =>
                        Predefined := Iir_Predefined_Ieee_1164_Scalar_Xnor;
                     when others =>
                        Predefined := Iir_Predefined_None;
                  end case;
                  Set_Implicit_Definition (Decl, Predefined);
               end;
            elsif Is_Scalar_Function (Decl)
              and then Get_Identifier (Decl) = Name_Not
            then
               Set_Implicit_Definition
                 (Decl, Iir_Predefined_Ieee_1164_Scalar_Not);
            elsif Is_Vector_Vector_Function (Decl) then
               declare
                  Predefined : Iir_Predefined_Functions;
               begin
                  case Get_Identifier (Decl) is
                     when Name_And =>
                        Predefined := Iir_Predefined_Ieee_1164_Vector_And;
                     when Name_Nand =>
                        Predefined := Iir_Predefined_Ieee_1164_Vector_Nand;
                     when Name_Or =>
                        Predefined := Iir_Predefined_Ieee_1164_Vector_Or;
                     when Name_Nor =>
                        Predefined := Iir_Predefined_Ieee_1164_Vector_Nor;
                     when Name_Xor =>
                        Predefined := Iir_Predefined_Ieee_1164_Vector_Xor;
                     when Name_Xnor =>
                        Predefined := Iir_Predefined_Ieee_1164_Vector_Xnor;
                     when others =>
                        Predefined := Iir_Predefined_None;
                  end case;
                  Set_Implicit_Definition (Decl, Predefined);
               end;
            elsif Is_Vector_Function (Decl)
              and then Get_Identifier (Decl) = Name_Not
            then
               Set_Implicit_Definition
                 (Decl, Iir_Predefined_Ieee_1164_Vector_Not);
            end if;
         end if;
      end loop;

      --  Since rising_edge and falling_edge do not read activity of its
      --  parameter, clear the flag to allow more optimizations.
      if Rising_Edge /= Null_Iir then
         Set_Has_Active_Flag
           (Get_Interface_Declaration_Chain (Rising_Edge), False);
      else
         raise Error;
      end if;
      if Falling_Edge /= Null_Iir then
         Set_Has_Active_Flag
           (Get_Interface_Declaration_Chain (Falling_Edge), False);
      else
         raise Error;
      end if;

   exception
      when Error =>
         Error_Msg_Sem (+Pkg, "package ieee.std_logic_1164 is ill-formed");

         --  Clear all definitions.
         Std_Logic_1164_Pkg := Null_Iir;
         Std_Ulogic_Type := Null_Iir;
         Std_Ulogic_Vector_Type := Null_Iir;
         Std_Logic_Type := Null_Iir;
         Std_Logic_Vector_Type := Null_Iir;
         Std_Ulogic_0 := Null_Iir;
         Std_Ulogic_1 := Null_Iir;
         Rising_Edge := Null_Iir;
         Falling_Edge := Null_Iir;
   end Extract_Declarations;
end Ieee.Std_Logic_1164;
