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

Module:

Author: Daniel Kroening, kroening@kroening.com

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

#ifndef CPROVER_STD_CODE_H
#define CPROVER_STD_CODE_H

#include <assert.h>

#include <expr.h>

class codet:public exprt
{
public:
  codet():exprt("code", typet("code"))
  {
  }
  
  codet(const irep_idt &statement):exprt("code", typet("code"))
  {
    set_statement(statement);
  }
  
  void set_statement(const irep_idt &statement)
  {
    set("statement", statement);
  }

  const irep_idt &get_statement() const
  {
    return get("statement");
  }
  
  codet &first_statement();
  const codet &first_statement() const;
  codet &last_statement();
  const codet &last_statement() const;
  class code_blockt &make_block();
  
  friend const codet &to_code(const exprt &expr)
  {
    assert(expr.id()=="code");
    return static_cast<const codet &>(expr);
  }

  friend codet &to_code(exprt &expr)
  {
    assert(expr.id()=="code");
    return static_cast<codet &>(expr);
  }
};

const codet &to_code(const exprt &expr);
codet &to_code(exprt &expr);

class code_blockt:public codet
{
public:
  code_blockt():codet("block")
  {
  }
  
  friend const code_blockt &to_code_block(const codet &code)
  {
    assert(code.get_statement()=="block");
    return static_cast<const code_blockt &>(code);
  }

  friend code_blockt &to_code_block(codet &code)
  {
    assert(code.get_statement()=="block");
    return static_cast<code_blockt &>(code);
  }
};

class code_skipt:public codet
{
public:
  code_skipt():codet("skip")
  {
  }
};

class code_assignt:public codet
{
public:
  code_assignt():codet("assign")
  {
    operands().reserve(2);
  }
  
  code_assignt(const exprt &lhs, const exprt &rhs):codet("assign")
  {
    copy_to_operands(lhs, rhs);
  }
  
  friend const code_assignt &to_code_assign(const codet &code)
  {
    assert(code.get_statement()=="assign");
    return static_cast<const code_assignt &>(code);
  }

  friend code_assignt &to_code_assign(codet &code)
  {
    assert(code.get_statement()=="assign");
    return static_cast<code_assignt &>(code);
  }
};

class code_assumet:public codet
{
public:
  code_assumet():codet("assume")
  {
    operands().reserve(1);
  }
  
  friend const code_assumet &to_code_assume(const codet &code)
  {
    assert(code.get_statement()=="assume");
    return static_cast<const code_assumet &>(code);
  }

  friend code_assumet &to_code_assume(codet &code)
  {
    assert(code.get_statement()=="assume");
    return static_cast<code_assumet &>(code);
  }
};

class code_assertt:public codet
{
public:
  code_assertt():codet("assert")
  {
    operands().reserve(1);
  }
  
  friend const code_assertt &to_code_assert(const codet &code)
  {
    assert(code.get_statement()=="assert");
    return static_cast<const code_assertt &>(code);
  }

  friend code_assertt &to_code_assert(codet &code)
  {
    assert(code.get_statement()=="assert");
    return static_cast<code_assertt &>(code);
  }
};

class code_ifthenelset:public codet
{
public:
  code_ifthenelset():codet("ifthenelse")
  {
    operands().reserve(3);
  }
  
  friend const code_ifthenelset &to_code_ifthenelse(const codet &code)
  {
    assert(code.get_statement()=="ifthenelse");
    return static_cast<const code_ifthenelset &>(code);
  }

  friend code_ifthenelset &to_code_ifthenelse(codet &code)
  {
    assert(code.get_statement()=="ifthenelse");
    return static_cast<code_ifthenelset &>(code);
  }
  
  const exprt &cond() const
  {
    return op0();
  }
};

class code_functioncallt:public codet
{
public:
  code_functioncallt():codet("functioncall")
  {
    operands().reserve(1);
  }
  
  friend const code_functioncallt &to_code_functioncall(const codet &code)
  {
    assert(code.get_statement()=="functioncall");
    return static_cast<const code_functioncallt &>(code);
  }

  friend code_functioncallt &to_code_functioncall(codet &code)
  {
    assert(code.get_statement()=="functioncall");
    return static_cast<code_functioncallt &>(code);
  }
};

class code_returnt:public codet
{
public:
  code_returnt():codet("return")
  {
    operands().reserve(1);
  }
  
  friend const code_returnt &to_code_return(const codet &code)
  {
    assert(code.get_statement()=="return");
    return static_cast<const code_returnt &>(code);
  }

  friend code_returnt &to_code_return(codet &code)
  {
    assert(code.get_statement()=="return");
    return static_cast<code_returnt &>(code);
  }
};

#endif
