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

Module: Value Set

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

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

#include <assert.h>

#include <context.h>
#include <simplify_expr.h>
#include <expr_util.h>
#include <base_type.h>

#include <langapi/language_util.h>
#include <ansi-c/c_types.h>

#include "value_set.h"

const hash_set_cont<exprt, irep_hash> empty_expr_set;
   
/*******************************************************************\

Function: value_sett::output

  Inputs:

 Outputs:

 Purpose:

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

void value_sett::output(
  const namespacet &ns,
  std::ostream &out) const
{
  for(valuest::const_iterator
      v_it=values.begin();
      v_it!=values.end();
      v_it++)
  {
    irep_idt identifier, display_name;
    
    const entryt &e=v_it->second;
  
    if(e.identifier=="value_set::dynamic_object")
    {
      display_name="DYNAMIC_OBJECT"+e.suffix;
      identifier="";
    }
    else
    {
      #if 0
      const symbolt &symbol=ns.lookup(id2string(e.identifier));
      display_name=symbol.display_name()+e.suffix;
      identifier=symbol.name;
      #else
      identifier=id2string(e.identifier);
      display_name=id2string(identifier)+e.suffix;
      #endif
    }
    
    out << display_name;

    out << " = \{ ";

    const value_sett::expr_sett &expr_set=
      e.expr_set.expr_set();
    
    unsigned width=0;
    
    for(value_sett::expr_sett::const_iterator
        s_it=expr_set.begin();
        s_it!=expr_set.end();
        s_it++)
    {
      std::string result=from_expr(ns, identifier, *s_it);
      out << result;
      width+=result.size();
    
      value_sett::expr_sett::const_iterator next(s_it);
      next++;

      if(next!=expr_set.end())
      {
        out << ", ";
        if(width>=40) out << "\n      ";
      }
    }

    out << " } " << std::endl;
  }
}

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

Function: operator+=

  Inputs:

 Outputs:

 Purpose:

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

static value_sett::expr_sett &operator+=(
  value_sett::expr_sett &dest,
  const value_sett::expr_sett &src)
{
  dest.insert(src.begin(), src.end());
  return dest;
}

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

Function: value_sett::make_union

  Inputs:

 Outputs:

 Purpose:

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

bool value_sett::make_union(const value_sett::valuest &new_values)
{
  bool result=false;
  
  for(valuest::const_iterator
      it=new_values.begin();
      it!=new_values.end();
      it++)
  {
    valuest::iterator it2=values.find(it->first);

    if(it2==values.end())
    {
      if(it->second.identifier=="value_set::dynamic_object")
      {
        values.insert(*it);
        result=true;
      }

      continue;
    }
      
    entryt &e=it2->second;
    const entryt &new_e=it->second;
    
    if(e.expr_set.make_union(new_e.expr_set))
      result=true;
  }
  
  return result;
}

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

Function: value_sett::get_value_set

  Inputs:

 Outputs:

 Purpose:

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

void value_sett::get_value_set(
  const exprt &expr,
  expr_sett &value_set,
  const namespacet &ns) const
{
  exprt tmp(expr);
  simplify(tmp);

  get_value_set_rec(tmp, value_set, "", ns);
}

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

Function: value_sett::get_value_set_rec

  Inputs:

 Outputs:

 Purpose:

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

void value_sett::get_value_set_rec(
  const exprt &expr,
  expr_sett &value_set,
  const std::string &suffix,
  const namespacet &ns) const
{
  if(expr.id()=="index")
  {
    assert(expr.operands().size()==2);

    const typet &type=ns.follow(expr.op0().type());

    assert(type.id()=="array" ||
           type.id()=="incomplete_array");
           
    get_value_set_rec(expr.op0(), value_set, suffix+"[]", ns);
    
    return;
  }
  else if(expr.id()=="member")
  {
    assert(expr.operands().size()==1);

    const typet &type=ns.follow(expr.op0().type());

    assert(type.id()=="struct" ||
           type.id()=="union" ||
           type.id()=="incomplete_struct" ||
           type.id()=="incomplete_union");
           
    const std::string &component_name=
      expr.get_string("component_name");
    
    get_value_set_rec(expr.op0(), value_set,
      suffix+"."+component_name, ns);
      
    return;
  }
  else if(expr.id()=="symbol")
  {
    // look it up
    valuest::const_iterator v_it=
      values.find(expr.get_string("identifier")+suffix);

    if(v_it!=values.end())
    {
      value_set+=v_it->second.expr_set.expr_set();
      return;
    }
  }
  else if(expr.id()=="if")
  {
    if(expr.operands().size()!=3)
      throw "if takes three operands";

    get_value_set_rec(expr.op1(), value_set, suffix, ns);
    get_value_set_rec(expr.op2(), value_set, suffix, ns);

    return;
  }
  else if(expr.id()=="address_of" ||
          expr.id()=="implicit_address_of")
  {
    if(expr.operands().size()!=1)
      throw expr.id_string()+" expected to have one operand";
      
    expr_sett reference_set;
    get_reference_set_rec(expr.op0(), reference_set, ns);
    
    for(expr_sett::const_iterator
        a_it=reference_set.begin();
        a_it!=reference_set.end();
        a_it++)
    {
      exprt value;

      if(a_it->id()=="unknown" ||
         a_it->id()=="invalid")
        value=exprt("unknown", expr.type());
      else
      {
        value=exprt(expr.id(), expr.type());
        value.copy_to_operands(*a_it);
      }

      value_set.insert(value);
    }
    
    return;
  }
  else if(expr.id()=="dereference" ||
          expr.id()=="implicit_dereference")
  {
    expr_sett reference_set;
    
    get_reference_set_rec(expr, reference_set, ns);
 
    if(!reference_set.empty())
    {
      for(expr_sett::const_iterator
          it=reference_set.begin();
          it!=reference_set.end();
          it++)
      {
        if(it->id()=="unknown" ||
           it->id()=="invalid")
          value_set.insert(exprt("unknown", expr.type()));
        else
          get_value_set_rec(*it, value_set, suffix, ns);
      }

      return;
    }
  }
  else if(expr.id()=="reference_to")
  {
    if(expr.operands().size()!=1)
      throw "reference_to expected to have one operand";
      
    expr_sett reference_set;
    get_reference_set_rec(expr.op0(), reference_set, ns);

    if(!reference_set.empty())
    {    
      for(expr_sett::const_iterator
          a_it=reference_set.begin();
          a_it!=reference_set.end();
          a_it++)
      {
        if(a_it->id()=="unknown" ||
           a_it->id()=="invalid")
          value_set.insert(exprt("unknown", expr.type()));
        else if(a_it->id()=="index")
        {
          if(a_it->operands().size()!=2)
            throw "index expects two operands";
            
          const typet &op_type=ns.follow(a_it->op0().type());
          
          assert(op_type.id()=="array" ||
                 op_type.id()=="incomplete_array");

          exprt index_expr(*a_it);

          // turn a[i] into a[*]
          if(!index_expr.op1().is_constant())
            index_expr.op1()=exprt("unknown", index_expr.op1().type());
              
          exprt value=exprt(expr.id(), expr.type());
          value.move_to_operands(index_expr);
              
          value_set.insert(value);
        }
        else
        {
          exprt value=exprt(expr.id(), expr.type());
          value.copy_to_operands(*a_it);
          value_set.insert(value);
        }
      }
      
      return;
    }
  }
  else if(expr.is_constant())
  {
    value_set.insert(expr);
    return;
  }
  else if(expr.id()=="typecast")
  {
    if(expr.operands().size()!=1)
      throw "typecast takes one operand";

    expr_sett tmp_set;
    get_value_set_rec(expr.op0(), tmp_set, suffix, ns);

    for(expr_sett::const_iterator
        it=tmp_set.begin();
        it!=tmp_set.end(); it++)
    {
      if(it->id()=="unknown" ||
         it->id()=="invalid")
        value_set.insert(exprt("unknown", expr.type()));
      else
      {
        // typecast of typecast?
        if(it->id()=="typecast")
        {
          assert(it->operands().size()==1);

          if(expr.type()==it->op0().type())
          {
            // redundant typecast
            value_set.insert(it->op0());
          }
          else if(expr.type().id()=="pointer" &&
                  it->type().id()=="pointer")
          {
            // pointer to pointer
            exprt tmp("typecast", expr.type());
            tmp.copy_to_operands(it->op0());
            value_set.insert(tmp); 
          }
          else
          {
            // to avoid infinite looping
            value_set.insert(exprt("unknown", expr.type()));
          }
        }
        else if(it->type()==expr.type())
        {
          // unnecessary typecast
          value_set.insert(*it);
        }
        else
        {
          exprt tmp("typecast", expr.type());
          tmp.copy_to_operands(*it);
          value_set.insert(tmp);
        }
      }
    }
    
    return;
  }
  else if(expr.id()=="+" || expr.id()=="-")
  {
    if(expr.operands().size()<2)
      throw expr.id_string()+" expected to have at least two operands";

    if(expr.type().id()=="pointer")
    {
      // find the pointer operand
      const exprt *ptr_operand=NULL;

      forall_operands(it, expr)
        if(it->type().id()=="pointer")
        {
          if(ptr_operand==NULL)
            ptr_operand=&(*it);
          else
            throw "more than one pointer operand in pointer arithmetic";
        }

      if(ptr_operand==NULL)
        throw "pointer type sum expected to have pointer operand";

      expr_sett pointer_expr_set;
      get_value_set_rec(*ptr_operand, pointer_expr_set, "", ns);

      for(expr_sett::const_iterator
          it=pointer_expr_set.begin();
          it!=pointer_expr_set.end();
          it++)
      {
        if(it->id()=="+" &&
           it->operands().size()==2 &&
           it->op1().id()=="unknown")
          value_set.insert(*it);
        else
        {
          exprt plus_unknown_expr("+", expr.type());
          plus_unknown_expr.copy_to_operands(
            *it, exprt("unknown", index_type()));

          value_set.insert(plus_unknown_expr);
        }
      }

      return;
    }
  }
  else if(expr.id()=="sideeffect")
  {
    const irep_idt &statement=expr.get("statement");
    
    if(statement=="functioncall")
    {
      if(expr.operands().size()<1)
        throw "functioncall takes at least one argument";

      if(expr.op0().id()=="symbol")
      {
      }
    }
    else if(statement=="malloc")
    {
      if(expr.type().id()!="pointer")
        throw "malloc expected to return pointer type";
      
      assert(suffix=="");

      exprt dynamic_object("dynamic_object", static_cast<const typet &>(expr.find("#type")));
      exprt address_of("address_of", expr.type());
      address_of.move_to_operands(dynamic_object);
      value_set.insert(address_of);
      return;          
    }
    else if(statement=="cpp_new" ||
            statement=="cpp_new[]")
    {
      assert(suffix=="");
      assert(expr.type().id()=="pointer");

      exprt dynamic_object("dynamic_object", expr.type().subtype());
      exprt address_of("address_of", expr.type());
      address_of.move_to_operands(dynamic_object);
      value_set.insert(address_of);
      return;
    }
  }
  else if(expr.id()=="with" ||
          expr.id()=="struct" ||
          expr.id()=="array_of" ||
          expr.id()=="array")
  {
    // these are supposed to be done by assign()
    throw "unexpected value in get_value_set: "+expr.id_string();
  }

  value_set.insert(exprt("unknown", expr.type()));
}

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

Function: value_sett::dereference_rec

  Inputs:

 Outputs:

 Purpose:

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

void value_sett::dereference_rec(
  const exprt &src,
  exprt &dest) const
{
  // remove pointer typecasts
  if(src.id()=="typecast")
  {
    assert(src.type().id()=="pointer");

    if(src.operands().size()!=1)
      throw "typecast expects one operand";
    
    dereference_rec(src.op0(), dest);
  }
  else
    dest=src;
}

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

Function: value_sett::get_reference_set_rec

  Inputs:

 Outputs:

 Purpose:

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

void value_sett::get_reference_set_rec(
  const exprt &expr,
  expr_sett &reference_set,
  const namespacet &ns) const
{
  if(expr.id()=="symbol")
  {
    reference_set.insert(expr);
    return;
  }
  else if(expr.id()=="dynamic_object")
  {
    reference_set.insert(expr);
    return;
  }
  else if(expr.id()=="string-constant")
  {
    reference_set.insert(expr);
    return;
  }
  else if(expr.id()=="dereference" ||
          expr.id()=="implicit_dereference")
  {
    if(expr.operands().size()!=1)
      throw expr.id_string()+" expected to have one operand";

    expr_sett value_set;
    get_value_set_rec(expr.op0(), value_set, "", ns);
    
    if(!value_set.empty())
    {
      for(expr_sett::const_iterator
          it=value_set.begin();
          it!=value_set.end();
          it++)
      {
        exprt tmp;
        
        dereference_rec(*it, tmp);
      
        if(tmp.id()=="address_of" ||
           tmp.id()=="implicit_address_of" ||
           tmp.id()=="reference_to")
        {
          if(tmp.operands().size()!=1)
            throw tmp.id_string()+" expected to have one operand";

          expr_sett tmp_set;
          get_reference_set_rec(tmp.op0(), tmp_set, ns);
          reference_set.insert(tmp_set.begin(), tmp_set.end());
        }
        else
          reference_set.insert(exprt("unknown", expr.type()));
      }
    }

    return;
  }
  else if(expr.id()=="index")
  {
    if(expr.operands().size()!=2)
      throw "index expected to have two operands";
  
    const exprt &array=expr.op0();
    const exprt &offset=expr.op1();
    const typet &array_type=ns.follow(array.type());
    
    assert(array_type.id()=="array" ||
           array_type.id()=="incomplete_array");

    expr_sett array_references;
    get_reference_set_rec(array, array_references, ns);
    
    for(expr_sett::const_iterator
        a_it=array_references.begin();
        a_it!=array_references.end();
        a_it++)
    {
      if(a_it->id()=="unknown" ||
         a_it->id()=="invalid")
      {
        reference_set.insert(exprt("unknown", expr.type()));
      }
      else
      {
        exprt value("index", expr.type());
        value.reserve_operands(2);
        value.copy_to_operands(*a_it);

        if(expr.op1().is_constant())
          value.copy_to_operands(expr.op1());
        else
          value.copy_to_operands(exprt("unknown", offset.type()));

        reference_set.insert(value);
      }
    }
    
    return;
  }
  else if(expr.id()=="member")
  {
    const irep_idt &component_name=expr.get("component_name");

    if(expr.operands().size()!=1)
      throw "member expected to have one operand";
  
    const exprt &struct_op=expr.op0();
    
    expr_sett struct_references;
    get_reference_set_rec(struct_op, struct_references, ns);

    for(expr_sett::const_iterator
        it=struct_references.begin();
        it!=struct_references.end();
        it++)
    {
      if(it->id()=="invalid" ||
         it->id()=="unknown")
      {
        reference_set.insert(exprt("unknown", expr.type()));
      }
      else
      {
        exprt value("member", expr.type());
        value.copy_to_operands(*it);
        value.set("component_name", component_name);
        reference_set.insert(value);
      }
    }

    return;
  }
  else if(expr.id()=="if")
  {
    if(expr.operands().size()!=3)
      throw "if takes three operands";

    get_reference_set_rec(expr.op1(), reference_set, ns);
    get_reference_set_rec(expr.op2(), reference_set, ns);
    return;
  }

  reference_set.insert(exprt("unknown", expr.type()));
}

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

Function: value_sett::assign

  Inputs:

 Outputs:

 Purpose:

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

void value_sett::assign(
  const exprt &lhs,
  const exprt &rhs,
  const namespacet &ns,
  bool add_to_sets)
{
  #if 0
  std::cout << "ASSIGN LHS: " << from_expr(ns, "", lhs) << std::endl;
  std::cout << "ASSIGN RHS: " << from_expr(ns, "", rhs) << std::endl;
  #endif

  if(rhs.id()=="if")
  {
    if(rhs.operands().size()!=3)
      throw "if takes three operands";

    assign(lhs, rhs.op1(), ns, add_to_sets);
    assign(lhs, rhs.op2(), ns, true);
    return;
  }

  const typet &type=ns.follow(lhs.type());
  
  if(type.id()=="struct" ||
     type.id()=="union")
  {
    const struct_typet &struct_type=to_struct_type(type);
    
    unsigned no=0;
    
    for(struct_typet::componentst::const_iterator
        c_it=struct_type.components().begin();
        c_it!=struct_type.components().end();
        c_it++, no++)
    {
      const typet &subtype=c_it->type();
      const irep_idt &name=c_it->get("name");

      // ignore methods
      if(subtype.id()=="code") continue;
    
      exprt lhs_member("member", subtype);
      lhs_member.set("component_name", name);
      lhs_member.copy_to_operands(lhs);

      exprt rhs_member;
    
      if(rhs.id()=="unknown" ||
         rhs.id()=="invalid")
      {
        rhs_member=exprt(rhs.id(), subtype);
      }
      else
      {
        assert(base_type_eq(rhs.type(), type, ns));
      
        if(rhs.id()=="struct" ||
           rhs.id()=="constant")
        {
          assert(no<rhs.operands().size());
          rhs_member=rhs.operands()[no];
        }
        else if(rhs.id()=="with")
        {
          assert(rhs.operands().size()==3);

          // see if op1 is the member we want
          const exprt &member_operand=rhs.op1();

          const irep_idt &component_name=
            member_operand.get("component_name");

          if(component_name==name)
          {
            // yes! just take op2
            rhs_member=rhs.op2();
          }
          else
          {
            // no! do op0
            rhs_member=exprt("member", subtype);
            rhs_member.copy_to_operands(rhs.op0());
            rhs_member.set("component_name", name);
          }
        }
        else
        {
          rhs_member=exprt("member", subtype);
          rhs_member.copy_to_operands(rhs);
          rhs_member.set("component_name", name);
        }

        assign(lhs_member, rhs_member, ns, add_to_sets);
      }
    }
  }
  else if(type.id()=="array")
  {
    exprt lhs_index("index", type.subtype());
    lhs_index.copy_to_operands(lhs, exprt("unknown", index_type()));

    if(rhs.id()=="unknown" ||
       rhs.id()=="invalid")
    {
      assign(lhs_index, exprt(rhs.id(), type.subtype()), ns, add_to_sets);
    }
    else
    {
      assert(base_type_eq(rhs.type(), type, ns));
        
      if(rhs.id()=="array_of")
      {
        assert(rhs.operands().size()==1);
        assign(lhs_index, rhs.op0(), ns, add_to_sets);
      }
      else if(rhs.id()=="array" ||
              rhs.id()=="constant")
      {
        forall_operands(o_it, rhs)
        {
          assign(lhs_index, *o_it, ns, add_to_sets);
          add_to_sets=true;
        }
      }
      else if(rhs.id()=="with")
      {
        assert(rhs.operands().size()==3);

        exprt op0_index("index", type.subtype());
        op0_index.copy_to_operands(rhs.op0(), exprt("unknown", index_type()));

        assign(lhs_index, op0_index, ns, add_to_sets);
        assign(lhs_index, rhs.op2(), ns, true);
      }
      else
      {
        exprt rhs_index("index", type.subtype());
        rhs_index.copy_to_operands(rhs, exprt("unknown", index_type()));
        assign(lhs_index, rhs_index, ns, true);
      }
    }
  }
  else
  {
    // basic type
    expr_sett values_rhs;
    
    get_value_set(rhs, values_rhs, ns);
    
    assign_rec(lhs, values_rhs, "", ns, add_to_sets);
  }
}

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

Function: value_sett::assign_rec

  Inputs:

 Outputs:

 Purpose:

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

void value_sett::assign_rec(
  const exprt &lhs,
  expr_sett &values_rhs,
  const std::string &suffix,
  const namespacet &ns,
  bool add_to_sets)
{
  if(lhs.id()=="symbol")
  {
    const irep_idt &identifier=lhs.get("identifier");
    
    if(add_to_sets)
      get_entry(identifier, suffix).expr_set.make_union(values_rhs);
    else
      get_entry(identifier, suffix).expr_set.move(values_rhs);
  }
  else if(lhs.id()=="dynamic_object")
  {
    const irep_idt identifier=
      "value_set::dynamic_object";

    get_entry(identifier, suffix).expr_set.make_union(values_rhs);
  }
  else if(lhs.id()=="dereference" ||
          lhs.id()=="implicit_dereference")
  {
    if(lhs.operands().size()!=1)
      throw lhs.id_string()+" expected to have one operand";

    expr_sett reference_set;
    get_reference_set_rec(lhs, reference_set, ns);
    
    if(reference_set.size()!=1)
      add_to_sets=true;
      
    for(expr_sett::const_iterator
        it=reference_set.begin();
        it!=reference_set.end();
        it++)
    {
      if(it->id()!="unknown" &&
         it->id()!="invalid")
        assign_rec(*it, values_rhs, suffix, ns, add_to_sets);
    }
  }
  else if(lhs.id()=="index")
  {
    if(lhs.operands().size()!=2)
      throw "index expected to have two operands";
      
    const typet &type=ns.follow(lhs.op0().type());
      
    assert(type.id()=="array" || type.id()=="incomplete_array");

    assign_rec(lhs.op0(), values_rhs, suffix+"[]", ns, true);
  }
  else if(lhs.id()=="member")
  {
    if(lhs.operands().size()!=1)
      throw "member expected to have one operand";
  
    const std::string &component_name=lhs.get_string("component_name");

    const typet &type=ns.follow(lhs.op0().type());

    assert(type.id()=="struct" ||
           type.id()=="union" ||
           type.id()=="incomplete_struct" ||
           type.id()=="incomplete_union");
           
    assign_rec(lhs.op0(), values_rhs, suffix+"."+component_name, ns, add_to_sets);
  }
  else if(lhs.id()=="valid_object" ||
          lhs.id()=="dynamic_size" ||
          lhs.id()=="dynamic_type")
  {
    // we ignore this here
  }
  else
    throw "assign NYI: "+lhs.id_string();
}

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

Function: value_sett::do_functioncall_rec

  Inputs:

 Outputs:

 Purpose:

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

void value_sett::do_functioncall_rec(
  const exprt &lhs,
  const exprt &function,
  const exprt &call,
  const namespacet &ns)
{
  if(function.id()=="symbol")
  {
    const code_typet &type=to_code_type(function.type());
    const code_typet::argumentst &arguments=type.arguments();

    unsigned i=1;

    for(code_typet::argumentst::const_iterator
        it=arguments.begin();
        it!=arguments.end();
        it++)
    {
      const irep_idt &identifier=it->get_identifier();
      
      if(identifier!="")
      {
        add_var(identifier, "");
      
        if(call.operands().size()<=i)
          throw "value_sett: not enough arguments";

        const exprt &v_expr=call.operands()[i];
        
        exprt lhs("symbol", it->type());
        lhs.set("identifier", identifier);
        
        assign(lhs, v_expr, ns, true);

        i++;
      }
    }
  }
  else if(function.id()=="if")
  {
    if(function.operands().size()!=3)
      throw "if expects three arguments";

    do_functioncall_rec(lhs, function.op1(), call, ns);
    do_functioncall_rec(lhs, function.op2(), call, ns);
  }
  else
    throw "value_sett: unexpected function: "+
          function.id_string();
}

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

Function: value_sett::do_return

  Inputs:

 Outputs:

 Purpose:

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

void value_sett::do_return(
  const exprt &lhs,
  const exprt &code,
  const namespacet &ns)
{
  if(lhs.is_nil()) return;

  assert(code.get("statement")=="return");

  if(code.operands().size()!=1) return;

  assign(lhs, code.op0(), ns);
}

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

Function: value_sett::apply_code

  Inputs:

 Outputs:

 Purpose:

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

void value_sett::apply_code(
  const exprt &code,
  const namespacet &ns)
{
  const irep_idt &statement=code.get("statement");

  if(statement=="block")
  {
    forall_operands(it, code)
      apply_code(*it, ns);
  }
  else if(statement=="functioncall")
  {
    if(code.operands().size()<1)
      throw "functioncall takes at least one argument";

    do_functioncall_rec(
      static_cast<const exprt &>(nil_rep), // lhs
      code.op0(),                          // function
      code,                                // call
      ns);
  }
  else if(statement=="assign" &&
          code.operands().size()==2 &&
          code.op1().id()=="sideeffect" &&
          code.op1().get("statement")=="functioncall")
  {
    if(code.op1().operands().size()<1)
      throw "functioncall takes at least one argument";

    do_functioncall_rec(
      code.op0(),       // lhs
      code.op1().op0(), // function
      code.op1(),       // call
      ns);
  }
  else if(statement=="assign" ||
          statement=="init")
  {
    if(code.operands().size()!=2)
      throw "assignment expected to have two operands";

    assign(code.op0(), code.op1(), ns);
  }
  else if(statement=="decl")
  {
    if(code.operands().size()!=1 &&
       code.operands().size()!=2)
      throw "decl expected to have one or two operands";

    const exprt &lhs=code.op0();

    if(lhs.id()!="symbol")
      throw "decl expected to have symbol on lhs";

    if(code.operands().size()==2)
      assign(lhs, code.op1(), ns);
    else
      assign(lhs, exprt("invalid", lhs.type()), ns);
  }
  else if(statement=="specc_notify" ||
          statement=="specc_wait")
  {
    // ignore, does not change variables
  }
  else if(statement=="function_pointer_call")
  {
    // may be a desaster
    //make_any();
    // TODO: ignore nondet_, don't use make_any
    // (convergence), but add unknown to all
    // entries in value set
  }
  else if(statement=="expression")
  {
    // can be ignored, we don't expect sideeffects here
  }
  else if(statement=="cpp_delete" ||
          statement=="cpp_delete[]")
  {
    // does nothing
  }
  else if(statement=="lock" || statement=="unlock")
  {
    // ignore for now
  }
  else if(statement=="nondet")
  {
    // doesn't do anything
  }
  else if(statement=="printf")
  {
    // doesn't do anything
  }
  else
  {
    std::cerr << code.pretty() << std::endl;
    throw "value_sett: unexpected statement: "+id2string(statement);
  }
}
