//
// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
//
// PolyMutator.cc --
//
// Author           : Richard C. Bilson
// Created On       : Mon May 18 07:44:20 2015
// Last Modified By : Peter A. Buhr
// Last Modified On : Thu Aug  4 11:26:22 2016
// Update Count     : 16
//

#include "PolyMutator.h"
#include "SynTree/Declaration.h"
#include "SynTree/Type.h"
#include "SynTree/Expression.h"
#include "SynTree/Statement.h"
#include "SynTree/Mutator.h"
#include "SynTree/Initializer.h"

namespace GenPoly {
	PolyMutator::PolyMutator() : scopeTyVars( TypeDecl::Data{} ), env( 0 ) {}

	void PolyMutator::mutateStatementList( std::list< Statement* > &statements ) {
		SemanticError errors;

		for ( std::list< Statement* >::iterator i = statements.begin(); i != statements.end(); ++i ) {
			if ( ! stmtsToAddAfter.empty() ) {
				statements.splice( i, stmtsToAddAfter );
			} // if
			try {
				*i = (*i)->acceptMutator( *this );
			} catch ( SemanticError &e ) {
				errors.append( e );
			} // try
			if ( ! stmtsToAdd.empty() ) {
				statements.splice( i, stmtsToAdd );
			} // if
		} // for
		if ( ! stmtsToAddAfter.empty() ) {
			statements.splice( statements.end(), stmtsToAddAfter );
		} // if
		if ( ! errors.isEmpty() ) {
			throw errors;
		}
	}

	Statement * PolyMutator::mutateStatement( Statement *stmt ) {
		Statement *newStmt = maybeMutate( stmt, *this );
		if ( ! stmtsToAdd.empty() || ! stmtsToAddAfter.empty() ) {
			CompoundStmt *compound = new CompoundStmt( noLabels );
			compound->get_kids().splice( compound->get_kids().end(), stmtsToAdd );
			compound->get_kids().push_back( newStmt );
			compound->get_kids().splice( compound->get_kids().end(), stmtsToAddAfter );
			// doEndScope();
			return compound;
		} else {
			return newStmt;
		}
	}

	Expression * PolyMutator::mutateExpression( Expression *expr ) {
		if ( expr ) {
			if ( expr->get_env() ) {
				env = expr->get_env();
			}
			// xxx - should env be cloned (or moved) onto the result of the mutate?
			return expr->acceptMutator( *this );
		} else {
			return expr;
		}
	}

	CompoundStmt * PolyMutator::mutate(CompoundStmt *compoundStmt) {
		doBeginScope();
		mutateStatementList( compoundStmt->get_kids() );
		doEndScope();
		return compoundStmt;
	}

	Statement * PolyMutator::mutate(IfStmt *ifStmt) {
		ifStmt->set_thenPart(  mutateStatement( ifStmt->get_thenPart() ) );
		ifStmt->set_elsePart(  mutateStatement( ifStmt->get_elsePart() ) );
		ifStmt->set_condition(  mutateExpression( ifStmt->get_condition() ) );
		return ifStmt;
	}

	Statement * PolyMutator::mutate(WhileStmt *whileStmt) {
		whileStmt->set_body(  mutateStatement( whileStmt->get_body() ) );
		whileStmt->set_condition(  mutateExpression( whileStmt->get_condition() ) );
		return whileStmt;
	}

	Statement * PolyMutator::mutate(ForStmt *forStmt) {
		forStmt->set_body(  mutateStatement( forStmt->get_body() ) );
		mutateAll( forStmt->get_initialization(), *this );
		forStmt->set_condition(  mutateExpression( forStmt->get_condition() ) );
		forStmt->set_increment(  mutateExpression( forStmt->get_increment() ) );
		return forStmt;
	}

	Statement * PolyMutator::mutate(SwitchStmt *switchStmt) {
		mutateStatementList( switchStmt->get_statements() );
		switchStmt->set_condition( mutateExpression( switchStmt->get_condition() ) );
		return switchStmt;
	}

	Statement * PolyMutator::mutate(CaseStmt *caseStmt) {
		mutateStatementList( caseStmt->get_statements() );
		caseStmt->set_condition(  mutateExpression( caseStmt->get_condition() ) );
		return caseStmt;
	}

	Statement * PolyMutator::mutate(TryStmt *tryStmt) {
		tryStmt->set_block(  maybeMutate( tryStmt->get_block(), *this ) );
		mutateAll( tryStmt->get_catchers(), *this );
		return tryStmt;
	}

	Statement * PolyMutator::mutate(CatchStmt *cathStmt) {
		cathStmt->set_body(  mutateStatement( cathStmt->get_body() ) );
		cathStmt->set_decl(  maybeMutate( cathStmt->get_decl(), *this ) );
		return cathStmt;
	}

	Statement * PolyMutator::mutate(ReturnStmt *retStmt) {
		retStmt->set_expr( mutateExpression( retStmt->get_expr() ) );
		return retStmt;
	}

	Statement * PolyMutator::mutate(ExprStmt *exprStmt) {
		exprStmt->set_expr( mutateExpression( exprStmt->get_expr() ) );
		return exprStmt;
	}


	Expression * PolyMutator::mutate(UntypedExpr *untypedExpr) {
		for ( std::list< Expression* >::iterator i = untypedExpr->get_args().begin(); i != untypedExpr->get_args().end(); ++i ) {
			*i = mutateExpression( *i );
		} // for
		return untypedExpr;
	}

	Expression *PolyMutator::mutate( StmtExpr * stmtExpr ) {
		// don't want statements from outer CompoundStmts to be added to this StmtExpr
		ValueGuard< std::list< Statement* > > oldStmtsToAdd( stmtsToAdd );
		ValueGuard< std::list< Statement* > > oldStmtsToAddAfter( stmtsToAddAfter );
		ValueGuard< TypeSubstitution * > oldEnv( env );

		// xxx - not sure if this is needed, along with appropriate reset, but I don't think so...
		// ValueGuard< TyVarMap > oldScopeTyVars( scopeTyVars );

		stmtsToAdd.clear();
		stmtsToAddAfter.clear();
		// scopeTyVars.clear();

		return Parent::mutate( stmtExpr );
	}

	Initializer *PolyMutator::mutate( SingleInit *singleInit ) {
		singleInit->set_value( mutateExpression( singleInit->get_value() ) );
		return singleInit;
	}
} // namespace GenPoly

// Local Variables: //
// tab-width: 4 //
// mode: c++ //
// compile-command: "make install" //
// End: //
