/*
 * This file is part of the Cforall project
 *
 * $Id: MakeLibCfa.cc,v 1.6 2005/08/29 20:14:14 rcbilson Exp $
 *
 */

#include "MakeLibCfa.h"
#include "SynTree/Declaration.h"
#include "SynTree/Type.h"
#include "SynTree/Expression.h"
#include "SynTree/Statement.h"
#include "SynTree/Initializer.h"
#include "SynTree/Visitor.h"
#include "CodeGen/OperatorTable.h"
#include "UniqueName.h"


namespace LibCfa {

class MakeLibCfa : public Visitor
{
public:
  void visit( FunctionDecl* funcDecl );
  void visit( ObjectDecl* objDecl );
  
  std::list< Declaration* > &get_newDecls() { return newDecls; }
  
private:
  std::list< Declaration* > newDecls;
};

void
makeLibCfa( std::list< Declaration* > &prelude )
{
  MakeLibCfa maker;
  acceptAll( prelude, maker );
  prelude.splice( prelude.end(), maker.get_newDecls() );
}

void 
MakeLibCfa::visit( FunctionDecl* origFuncDecl )
{
  if( origFuncDecl->get_linkage() != LinkageSpec::Intrinsic ) return;
  
  FunctionDecl *funcDecl = origFuncDecl->clone();
  CodeGen::OperatorInfo opInfo;
  bool lookResult = CodeGen::operatorLookup( funcDecl->get_name(), opInfo );
  assert( lookResult );
  assert( !funcDecl->get_statements() );
  UntypedExpr *newExpr = new UntypedExpr( new NameExpr( funcDecl->get_name() ) );
  UniqueName paramNamer( "_p" );
  std::list< DeclarationWithType* >::iterator param = funcDecl->get_functionType()->get_parameters().begin();
  assert( param != funcDecl->get_functionType()->get_parameters().end() );
  if( (*param)->get_name() == "" ) {
    (*param)->set_name( paramNamer.newName() );
    (*param)->set_linkage( LinkageSpec::C );
  }
  switch( opInfo.type ) {
  case CodeGen::OT_INDEX:
  case CodeGen::OT_CALL:
  case CodeGen::OT_PREFIX:
  case CodeGen::OT_POSTFIX:
  case CodeGen::OT_INFIX:
    newExpr->get_args().push_back( new VariableExpr( *param ) );
    break;
    
  case CodeGen::OT_PREFIXASSIGN:
  case CodeGen::OT_POSTFIXASSIGN:
  case CodeGen::OT_INFIXASSIGN:
  {
    newExpr->get_args().push_back( new VariableExpr( *param ) );
///     UntypedExpr *deref = new UntypedExpr( new NameExpr( "*?" ) );
///     deref->get_args().push_back( new VariableExpr( *param ) );
///     newExpr->get_args().push_back( deref );
    break;
  }
    
  case CodeGen::OT_CONSTANT:
    assert( false );
  }
  for( param++; param != funcDecl->get_functionType()->get_parameters().end(); ++param ) {
    if( (*param)->get_name() == "" ) {
      (*param)->set_name( paramNamer.newName() );
      (*param)->set_linkage( LinkageSpec::C );
    }
    newExpr->get_args().push_back( new VariableExpr( *param ) );
  }
  funcDecl->set_statements( new CompoundStmt( std::list< Label >() ) );
  funcDecl->get_statements()->get_kids().push_back( new ReturnStmt( std::list< Label >(), newExpr ) );
  newDecls.push_back( funcDecl );
}

void 
MakeLibCfa::visit( ObjectDecl* origObjDecl )
{
  if( origObjDecl->get_linkage() != LinkageSpec::Intrinsic ) return;
  
  ObjectDecl *objDecl = origObjDecl->clone();
  assert( !objDecl->get_init() );
  std::list< Expression* > noDesignators;
  objDecl->set_init( new SingleInit( new NameExpr( objDecl->get_name() ), noDesignators ) );
  newDecls.push_back( objDecl );
}

} // namespace LibCfa
