/*******************************************************************\

Module:

Author: Daniel Kroening, kroening@cs.cmu.edu

\*******************************************************************/

#ifndef CPROVER_STD_TYPES_H
#define CPROVER_STD_TYPES_H

#include <assert.h>

#include <type.h>
#include <expr.h>

class bool_typet:public typet
{
public:
  bool_typet()
  {
    id("bool");
  }
};

class empty_typet:public typet
{
public:
  empty_typet()
  {
    id("empty");
  }
};

// structs

class struct_union_typet:public typet
{
public:
  struct_union_typet()
  {
  }

  typedef exprt componentt;
  typedef exprt::operandst componentst;
  
  const componentst &components() const
  {
    return (const componentst &)find("components").get_sub();
  }
  
  componentst &components()
  {
    return (componentst &)add("components").get_sub();
  }
  
  const exprt *get_component(
    const irep_idt &component_name) const;

  bool component_number(
    const irep_idt &component_name,
    unsigned &number) const;

  bool component_type(
    const irep_idt &component_name,
    typet &dest_type) const;
};

class struct_typet:public struct_union_typet
{
public:
  struct_typet()
  {
    id("struct");
  }
    
  friend const struct_typet &to_struct_type(const typet &type)
  {
    assert(type.id()=="struct" ||
           type.id()=="union" ||
           type.id()=="class");
    return static_cast<const struct_typet &>(type);
  }

  friend struct_typet &to_struct_type(typet &type)
  {
    assert(type.id()=="struct" ||
           type.id()=="union" ||
           type.id()=="class");
    return static_cast<struct_typet &>(type);
  }

  bool is_prefix_of(const struct_typet &other) const
  {
    const componentst &ot_components=other.components();
    const componentst &tt_components=components();

    if(ot_components.size()<
       tt_components.size())
      return false; 

    componentst::const_iterator
      ot_it=ot_components.begin();

    forall_expr(tt_it, tt_components)
    {
      if(ot_it->type()!=tt_it->type() ||
         ot_it->get("name")!=tt_it->get("name"))
      {
        return false; // they just don't match
      }

      ot_it++;
    }

    return true; // ok, *this is a prefix of ot
  }
};

const struct_typet &to_struct_type(const typet &type);
struct_typet &to_struct_type(typet &type);

class union_typet:public struct_union_typet
{
public:
  union_typet()
  {
    id("union");
  }

  friend const union_typet &to_union_type(const typet &type)
  {
    assert(type.id()=="union");
    return static_cast<const union_typet &>(type);
  }

  friend union_typet &to_union_type(typet &type)
  {
    assert(type.id()=="union");
    return static_cast<union_typet &>(type);
  }
};

const union_typet &to_union_type(const typet &type);
union_typet &to_union_type(typet &type);

// functions

class code_typet:public typet
{
public:
  code_typet()
  {
    id("code");
  }
  
  friend const code_typet &to_code_type(const typet &type)
  {
    assert(type.id()=="code");
    return static_cast<const code_typet &>(type);
  }
  
  friend code_typet &to_code_type(typet &type)
  {
    assert(type.id()=="code");
    return static_cast<code_typet &>(type);
  }
  
  class argumentt:public exprt
  {
  public:
    argumentt():exprt("argument")
    {
    }
    
    argumentt(const typet &type):exprt("argument", type)
    {
    }
    
    const exprt &default_value() const
    {
      return find_expr("default_value");
    }

    bool has_default_value() const
    {
      return default_value().is_not_nil();
    }

    exprt &default_value()
    {
      return add_expr("default_value");
    }
    
    void set_identifier(const irep_idt &identifier)
    {
      set("#identifier", identifier);
    }

    void set_base_name(const irep_idt &name)
    {
      set("#base_name", name);
    }

    const irep_idt &get_identifier() const
    {
      return get("#identifier");
    }

    const irep_idt &get_base_name() const
    {
      return get("#base_name");
    }
  };
  
  bool has_ellipsis() const
  {
    return find("arguments").get_bool("ellipsis");
  }

  void make_ellipsis()
  {
    add("arguments").set("ellipsis", true);
  }

  typedef std::vector<argumentt> argumentst;

  const typet &return_type() const
  {
    return find_type("return_type");
  }

  typet &return_type()
  {
    return add_type("return_type");
  }

  const argumentst &arguments() const
  {
    return (const argumentst &)find("arguments").get_sub();
  }

  argumentst &arguments()
  {
    return (argumentst &)add("arguments").get_sub();
  }
};

const code_typet &to_code_type(const typet &type);
code_typet &to_code_type(typet &type);

class array_typet:public typet
{
public:
  array_typet()
  {
    id("array");
  }
  
  const exprt &size() const
  {
    return (const exprt &)find("size");
  }
  
  exprt &size()
  {
    return (exprt &)add("size");
  }

  friend const array_typet &to_array_type(const typet &type)
  {
    assert(type.id()=="array");
    return static_cast<const array_typet &>(type);
  }

  friend array_typet &to_array_type(typet &type)
  {
    assert(type.id()=="array");
    return static_cast<array_typet &>(type);
  }
};

const array_typet &to_array_type(const typet &type);
array_typet &to_array_type(typet &type);

class pointer_typet:public typet
{
public:
  pointer_typet()
  {
    id("pointer");
  }
};

class reference_typet:public pointer_typet
{
public:
  reference_typet()
  {
    set("#reference", true);
  }
};

bool is_reference(const typet &type);

class bv_typet:public typet
{
public:
  unsigned get_width() const
  {
    return atoi(get("width").c_str());
  }
};

class fixedbv_typet:public bv_typet
{
public:
  fixedbv_typet()
  {
    id("fixedbv");
  }

  friend const fixedbv_typet &to_fixedbv_type(const typet &type)
  {
    assert(type.id()=="fixedbv");
    return static_cast<const fixedbv_typet &>(type);
  }

  unsigned get_fraction_bits() const
  {
    unsigned width=get_width();
    const std::string &integer_bits=get_string("integer_bits");

    if(integer_bits=="") return width/2;
    return width-atoi(integer_bits.c_str());
  }
};

const fixedbv_typet &to_fixedbv_type(const typet &type);

#endif
