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

Module: Symbolic Execution of ANSI-C

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

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

#include <assert.h>

#include <expr_util.h>
#include <i2string.h>
#include <arith_tools.h>
#include <cprover_prefix.h>
#include <std_types.h>

#include <ansi-c/c_types.h>

#include "basic_symex.h"
#include "printf_formatter.h"

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

Function: basic_symext::symex_malloc

  Inputs:

 Outputs:

 Purpose:

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

void basic_symext::symex_malloc(
  statet &state,
  const exprt &lhs,
  const exprt &code)
{
  if(code.operands().size()!=1)
    throw "malloc expected to have one operand";
    
  if(lhs.is_nil())
    return; // ignore

  if(lhs.type().id()!="pointer")
    throw "malloc expected to assign to pointer";

  // size
  typet type=(const typet &)code.find("#type");
  exprt size=(const exprt &)code.find("#size");
  bool size_is_one;

  if(size.is_nil())
    size_is_one=true;
  else
  {
    state.rename(size);
    mp_integer i;
    size_is_one=(!to_integer(size, i) && i==1);
  }
  
  if(type.is_nil())
    type=char_type();

  dynamic_counter++;

  // value
  symbolt symbol;

  symbol.base_name="dynamic_"+
    i2string(dynamic_counter)+
    (size_is_one?"_value":"_array");

  symbol.name="symex_dynamic::"+id2string(symbol.base_name);
  symbol.lvalue=true;
  
  if(size_is_one)
    symbol.type=type;
  else
  {
    symbol.type=typet("array");
    symbol.type.subtype()=type;
    symbol.type.set("size", size);
  }

  symbol.type.set("#dynamic", true);

  symbol.mode="C";

  new_context.add(symbol);
  
  exprt rhs("address_of", typet("pointer"));
  
  if(size_is_one)
  {
    rhs.type().subtype()=symbol.type;
    rhs.copy_to_operands(symbol_expr(symbol));
  }
  else
  {
    exprt index_expr("index", symbol.type.subtype());
    index_expr.copy_to_operands(symbol_expr(symbol), gen_zero(int_type()));
    rhs.type().subtype()=symbol.type.subtype();
    rhs.move_to_operands(index_expr);
  }
  
  if(rhs.type()!=lhs.type())
    rhs.make_typecast(lhs.type());

  state.rename(rhs);
  
  guardt guard;
  symex_assign_rec(state, lhs, rhs, guard, false);
}

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

Function: basic_symext::symex_printf

  Inputs:

 Outputs:

 Purpose:

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

void basic_symext::symex_printf(
  statet &state,
  const exprt &lhs,
  const exprt &rhs)
{
  if(rhs.operands().empty())
    throw "printf expected to have at least one operand";

  printf_formattert printf_formatter;
  printf_formatter(rhs.operands());

  target.output(state.guard, state.source, printf_formatter.dest);
}

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

Function: basic_symext::symex_cpp_new

  Inputs:

 Outputs:

 Purpose:

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

void basic_symext::symex_cpp_new(
  statet &state,
  const exprt &lhs,
  const exprt &code)
{
  bool do_array;

  if(code.type().id()!="pointer")
    throw "new expected to return pointer";

  do_array=(code.get("statement")=="cpp_new[]");
      
  dynamic_counter++;

  const std::string count_string(i2string(dynamic_counter));

  // value
  symbolt symbol;
  symbol.base_name=
    do_array?"dynamic_"+count_string+"_array":
             "dynamic_"+count_string+"_value";
  symbol.name="symex_dynamic::"+id2string(symbol.base_name);
  symbol.lvalue=true;
  symbol.mode="C++";
  
  if(do_array)
  {
    symbol.type=array_typet();
    symbol.type.subtype()=code.type().subtype();
    symbol.type.set("size", code.find("size"));
  }
  else
    symbol.type=code.type().subtype();

  //symbol.type.set("#active", symbol_expr(active_symbol));
  symbol.type.set("#dynamic", true);
  
  new_context.add(symbol);

  // make symbol expression

  exprt rhs("address_of", typet("pointer"));
  rhs.type().subtype()=code.type().subtype();
  
  if(do_array)
  {
    exprt index_expr("index", code.type().subtype());
    index_expr.copy_to_operands(symbol_expr(symbol), gen_zero(int_type()));
    rhs.move_to_operands(index_expr);
  }
  else
    rhs.copy_to_operands(symbol_expr(symbol));
  
  state.rename(rhs);

  guardt guard;
  symex_assign_rec(state, lhs, rhs, guard, false);
}

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

Function: basic_symext::symex_cpp_delete

  Inputs:

 Outputs:

 Purpose:

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

void basic_symext::symex_cpp_delete(
  statet &state,
  const codet &code)
{
  //bool do_array=code.get("statement")=="delete[]";
}

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

Function: basic_symext::symex_nondet

  Inputs:

 Outputs:

 Purpose:

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

void basic_symext::symex_nondet(
  statet &state,
  const exprt &lhs,
  const exprt &code)
{
  exprt rhs("nondet_symbol", code.type());
  rhs.set("identifier", "symex::"+i2string(nondet_count++));
  rhs.location()=code.location();
  
  exprt new_lhs(lhs);
  read(new_lhs);

  guardt guard; // NOT the state guard!
  symex_assign_rec(state, new_lhs, rhs, guard, false);
}

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

Function: basic_symext::symex_fkt

  Inputs:

 Outputs:

 Purpose:

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

void basic_symext::symex_fkt(
  statet &state,
  const exprt &lhs,
  const exprt &code)
{
  #if 0
  exprt new_fc("function", fc.type());

  new_fc.reserve_operands(fc.operands().size()-1);

  bool first=true;

  Forall_operands(it, fc)
    if(first) first=false; else new_fc.move_to_operands(*it);

  new_fc.set("identifier", fc.op0().get("identifier"));

  fc.swap(new_fc);
  #endif
}

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

Function: basic_symext::symex_macro

  Inputs:

 Outputs:

 Purpose:

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

void basic_symext::symex_macro(
  statet &state,
  const exprt &lhs,
  const exprt &code)
{
  const irep_idt &identifier=code.op0().get("identifier");

  if(identifier==CPROVER_MACRO_PREFIX "waitfor")
  {
    #if 0
    exprt new_fc("waitfor", fc.type());

    if(fc.operands().size()!=4)
      throw "waitfor expected to have four operands";

    exprt &cycle_var=fc.op1();
    exprt &bound=fc.op2();
    exprt &predicate=fc.op3();

    if(cycle_var.id()!="symbol")
      throw "waitfor expects symbol as first operand but got "+
            cycle_var.id();

    exprt new_cycle_var(cycle_var);
    new_cycle_var.id("waitfor_symbol");
    new_cycle_var.copy_to_operands(bound);

    replace_expr(cycle_var, new_cycle_var, predicate);

    new_fc.operands().resize(4);
    new_fc.op0().swap(cycle_var);
    new_fc.op1().swap(new_cycle_var);
    new_fc.op2().swap(bound);
    new_fc.op3().swap(predicate);

    fc.swap(new_fc);
    #endif
  }
  else
    throw "unknown macro: "+id2string(identifier);
}
