text: /*******************************************************************\
  
  Module:
  
  Author: Daniel Kroening, kroening@cs.cmu.edu
  
  \*******************************************************************/
  
  #include "expr2cpp.h"
  
  #include <cassert>
  
  #include <util/std_types.h>
  #include <util/std_expr.h>
  #include <util/symbol.h>
  #include <util/lispirep.h>
  #include <util/lispexpr.h>
  #include <util/namespace.h>
  
  #include <ansi-c/c_misc.h>
  #include <ansi-c/c_qualifiers.h>
  #include <ansi-c/expr2c_class.h>
  
  #include "cpp_name.h"
  
  class expr2cppt:public expr2ct
  {
  public:
    explicit expr2cppt(const namespacet &_ns):expr2ct(_ns) { }
  
  protected:
    std::string convert_with_precedence(
      const exprt &src, unsigned &precedence) override;
    std::string convert_cpp_this();
    std::string convert_cpp_new(const exprt &src);
    std::string convert_extractbit(const exprt &src);
    std::string convert_extractbits(const exprt &src);
    std::string convert_code_cpp_delete(const exprt &src, unsigned indent);
    std::string convert_code_cpp_new(const exprt &src, unsigned indent);
    std::string convert_struct(const exprt &src, unsigned &precedence) override;
    std::string convert_code(const codet &src, unsigned indent) override;
    // NOLINTNEXTLINE(whitespace/line_length)
    std::string convert_constant(const constant_exprt &src, unsigned &precedence) override;
  
    std::string convert_rec(
      const typet &src,
      const qualifierst &qualifiers,
      const std::string &declarator) override;
  };
  
  std::string expr2cppt::convert_struct(
    const exprt &src,
    unsigned &precedence)
  {
    const typet &full_type=ns.follow(src.type());
  
    if(full_type.id()!=ID_struct)
      return convert_norep(src, precedence);
  
    const struct_typet &struct_type=to_struct_type(full_type);
  
    std::string dest="{ ";
  
    const struct_typet::componentst &components=
      struct_type.components();
  
    assert(components.size()==src.operands().size());
  
    exprt::operandst::const_iterator o_it=src.operands().begin();
  
    bool first=true;
    size_t last_size=0;
  
    for(const auto &c : components)
    {
      if(c.type().id() == ID_code)
      {
      }
      else
      {
        std::string tmp=convert(*o_it);
        std::string sep;
  
        if(first)
          first=false;
        else
        {
          if(last_size+40<dest.size())
          {
            sep=",\n    ";
            last_size=dest.size();
          }
          else
            sep=", ";
        }
  
        dest+=sep;
        dest+='.';
        dest += c.get_string(ID_pretty_name);
        dest+='=';
        dest+=tmp;
      }
  
      o_it++;
    }
  
    dest+=" }";
  
    return dest;
  }
  
  std::string expr2cppt::convert_constant(
    const constant_exprt &src,
    unsigned &precedence)
  {
    if(src.type().id() == ID_c_bool)
    {
      // C++ has built-in Boolean constants, in contrast to C
      if(src.is_true())
        return "true";
      else if(src.is_false())
        return "false";
    }
  
    return expr2ct::convert_constant(src, precedence);
  }
  
  std::string expr2cppt::convert_rec(
    const typet &src,
    const qualifierst &qualifiers,
    const std::string &declarator)
  {
    std::unique_ptr<qualifierst> clone = qualifiers.clone();
    qualifierst &new_qualifiers = *clone;
    new_qualifiers.read(src);
  
    const std::string d = declarator.empty() ? declarator : (" " + declarator);
  
    const std::string q=
      new_qualifiers.as_string();
  
    if(is_reference(src))
    {
      return q+convert(src.subtype())+" &"+d;
    }
    else if(is_rvalue_reference(src))
    {
      return q+convert(src.subtype())+" &&"+d;
    }
    else if(!src.get(ID_C_c_type).empty())
    {
      const irep_idt c_type=src.get(ID_C_c_type);
  
      if(c_type == ID_bool)
        return q+"bool"+d;
      else
        return expr2ct::convert_rec(src, qualifiers, declarator);
    }
    else if(src.id() == ID_struct)
    {
      std::string dest=q;
  
      if(src.get_bool(ID_C_class))
        dest+="class";
      else if(src.get_bool(ID_C_interface))
        dest+="__interface"; // MS-specific
      else
        dest+="struct";
  
      dest+=d;
  
      return dest;
    }
    else if(src.id() == ID_struct_tag)
    {
      const struct_typet &struct_type = ns.follow_tag(to_struct_tag_type(src));
  
      std::string dest = q;
  
      if(src.get_bool(ID_C_class))
        dest += "class";
      else if(src.get_bool(ID_C_interface))
        dest += "__interface"; // MS-specific
      else
        dest += "struct";
  
      const irept &tag = struct_type.find(ID_tag);
      if(!tag.id().empty())
      {
        if(tag.id() == ID_cpp_name)
          dest += " " + to_cpp_name(tag).to_string();
        else
          dest += " " + id2string(tag.id());
      }
  
      dest += d;
  
      return dest;
    }
    else if(src.id() == ID_union_tag)
    {
      const union_typet &union_type = ns.follow_tag(to_union_tag_type(src));
  
      std::string dest = q + "union";
  
      const irept &tag = union_type.find(ID_tag);
      if(!tag.id().empty())
      {
        if(tag.id() == ID_cpp_name)
          dest += " " + to_cpp_name(tag).to_string();
        else
          dest += " " + id2string(tag.id());
      }
  
      dest += d;
  
      return dest;
    }
    else if(src.id()==ID_constructor)
    {
      return "constructor ";
    }
    else if(src.id()==ID_destructor)
    {
      return "destructor ";
    }
    else if(src.id()=="cpp-template-type")
    {
      return "typename";
    }
    else if(src.id()==ID_template)
    {
      std::string dest="template<";
  
      const irept::subt &arguments=src.find(ID_arguments).get_sub();
  
      forall_irep(it, arguments)
      {
        if(it!=arguments.begin())
          dest+=", ";
  
        const exprt &argument=(const exprt &)*it;
  
        if(argument.id()==ID_symbol)
        {
          dest+=convert(argument.type())+" ";
          dest+=convert(argument);
        }
        else if(argument.id()==ID_type)
          dest+=convert(argument.type());
        else
        {
          lispexprt lisp;
          irep2lisp(argument, lisp);
          dest+="irep(\""+MetaString(lisp.expr2string())+"\")";
        }
      }
  
      dest+="> "+convert(src.subtype());
      return dest;
    }
    else if(src.id()==ID_pointer && src.subtype().id()==ID_nullptr)
    {
      return "std::nullptr_t";
    }
    else if(src.id() == ID_pointer && src.find(ID_to_member).is_not_nil())
    {
      typet tmp=src;
      typet member;
      member.swap(tmp.add(ID_to_member));
  
      std::string dest="("+convert_rec(member, c_qualifierst(), "")+":: *)";
  
      if(src.subtype().id()==ID_code)
      {
        const code_typet &code_type = to_code_type(src.subtype());
        const typet &return_type = code_type.return_type();
        dest=convert_rec(return_type, c_qualifierst(), "")+" "+dest;
  
        const code_typet::parameterst &args = code_type.parameters();
        dest+="(";
  
        for(code_typet::parameterst::const_iterator it=args.begin();
            it!=args.end();
            ++it)
        {
          if(it!=args.begin())
            dest+=", ";
          dest+=convert_rec(it->type(), c_qualifierst(), "");
        }
  
        dest+=")";
        dest+=d;
      }
      else
        dest=convert_rec(src.subtype(), c_qualifierst(), "")+" "+dest+d;
  
      return dest;
    }
    else if(src.id()==ID_verilog_signedbv ||
            src.id()==ID_verilog_unsignedbv)
      return "sc_lv[" + std::to_string(to_bitvector_type(src).get_width()) + "]" +
             d;
    else if(src.id()==ID_unassigned)
      return "?";
    else if(src.id()==ID_code)
    {
      const code_typet &code_type=to_code_type(src);
  
      // C doesn't really have syntax for function types,
      // so we use C++11 trailing return types!
  
      std::string dest="auto";
  
      // qualifiers, declarator?
      if(d.empty())
        dest+=' ';
      else
        dest+=d;
  
      dest+='(';
      const code_typet::parameterst &parameters=code_type.parameters();
  
      for(code_typet::parameterst::const_iterator
          it=parameters.begin();
          it!=parameters.end();
          it++)
      {
        if(it!=parameters.begin())
          dest+=", ";
  
        dest+=convert(it->type());
      }
  
      if(code_type.has_ellipsis())
      {
        if(!parameters.empty())
          dest+=", ";
        dest+="...";
      }
  
      dest+=')';
  
      const typet &return_type=code_type.return_type();
      dest+=" -> "+convert(return_type);
  
      return dest;
    }
    else if(src.id()==ID_initializer_list)
    {
      // only really used in error messages
      return "{ ... }";
    }
    else if(src.id() == ID_c_bool)
    {
      return q + "bool" + d;
    }
    else
      return expr2ct::convert_rec(src, qualifiers, declarator);
  }
  
  std::string expr2cppt::convert_cpp_this()
  {
    return id2string(ID_this);
  }
  
  std::string expr2cppt::convert_cpp_new(const exprt &src)
  {
    std::string dest;
  
    if(src.get(ID_statement)==ID_cpp_new_array)
    {
      dest="new";
  
      std::string tmp_size=
        convert(static_cast<const exprt &>(src.find(ID_size)));
  
      dest+=' ';
      dest+=convert(src.type().subtype());
      dest+='[';
      dest+=tmp_size;
      dest+=']';
    }
    else
      dest="new "+convert(src.type().subtype());
  
    return dest;
  }
  
  std::string expr2cppt::convert_code_cpp_new(const exprt &src, unsigned indent)
  {
    return indent_str(indent) + convert_cpp_new(src) + ";\n";
  }
  
  std::string expr2cppt::convert_code_cpp_delete(
    const exprt &src,
    unsigned indent)
  {
    std::string dest=indent_str(indent)+"delete ";
  
    if(src.operands().size()!=1)
    {
      unsigned precedence;
      return convert_norep(src, precedence);
    }
  
    std::string tmp = convert(to_unary_expr(src).op());
  
    dest+=tmp+";\n";
  
    return dest;
  }
  
  std::string expr2cppt::convert_with_precedence(
    const exprt &src,
    unsigned &precedence)
  {
    if(src.id()=="cpp-this")
    {
      precedence = 15;
      return convert_cpp_this();
    }
    if(src.id()==ID_extractbit)
    {
      precedence = 15;
      return convert_extractbit(src);
    }
    else if(src.id()==ID_extractbits)
    {
      precedence = 15;
      return convert_extractbits(src);
    }
    else if(src.id()==ID_side_effect &&
            (src.get(ID_statement)==ID_cpp_new ||
             src.get(ID_statement)==ID_cpp_new_array))
    {
      precedence = 15;
      return convert_cpp_new(src);
    }
    else if(src.id()==ID_side_effect &&
            src.get(ID_statement)==ID_throw)
    {
      precedence = 16;
      return convert_function(src, "throw");
    }
    else if(src.is_constant() && src.type().id()==ID_verilog_signedbv)
      return "'"+id2string(src.get(ID_value))+"'";
    else if(src.is_constant() && src.type().id()==ID_verilog_unsignedbv)
      return "'"+id2string(src.get(ID_value))+"'";
    else if(src.is_constant() && to_constant_expr(src).get_value()==ID_nullptr)
      return "nullptr";
    else if(src.id()==ID_unassigned)
      return "?";
    else if(src.id() == ID_pod_constructor)
      return "pod_constructor";
    else
      return expr2ct::convert_with_precedence(src, precedence);
  }
  
  std::string expr2cppt::convert_code(
    const codet &src,
    unsigned indent)
  {
    const irep_idt &statement=src.get(ID_statement);
  
    if(statement==ID_cpp_delete ||
       statement==ID_cpp_delete_array)
      return convert_code_cpp_delete(src, indent);
  
    if(statement==ID_cpp_new ||
       statement==ID_cpp_new_array)
      return convert_code_cpp_new(src, indent);
  
    return expr2ct::convert_code(src, indent);
  }
  
  std::string expr2cppt::convert_extractbit(const exprt &src)
  {
    const auto &extractbit_expr = to_extractbit_expr(src);
    return convert(extractbit_expr.op0()) + "[" + convert(extractbit_expr.op1()) +
           "]";
  }
  
  std::string expr2cppt::convert_extractbits(const exprt &src)
  {
    const auto &extractbits_expr = to_extractbits_expr(src);
    return convert(extractbits_expr.src()) + ".range(" +
           convert(extractbits_expr.upper()) + "," +
           convert(extractbits_expr.lower()) + ")";
  }
  
  std::string expr2cpp(const exprt &expr, const namespacet &ns)
  {
    expr2cppt expr2cpp(ns);
    expr2cpp.get_shorthands(expr);
    return expr2cpp.convert(expr);
  }
  
  std::string type2cpp(const typet &type, const namespacet &ns)
  {
    expr2cppt expr2cpp(ns);
    return expr2cpp.convert(type);
  }
  