//
// 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.
//
// GenType.cc --
//
// Author           : Richard C. Bilson
// Created On       : Mon May 18 07:44:20 2015
// Last Modified By : Peter A. Buhr
// Last Modified On : Fri Mar 17 09:02:28 2017
// Update Count     : 22
//
#include "GenType.h"

#include <cassert>                // for assert, assertf
#include <list>                   // for _List_iterator, _List_const_iterator
#include <sstream>                // for operator<<, ostringstream, basic_os...

#include "CodeGenerator.h"        // for CodeGenerator
#include "SynTree/Declaration.h"  // for DeclarationWithType
#include "SynTree/Expression.h"   // for Expression
#include "SynTree/Type.h"         // for PointerType, Type, FunctionType
#include "SynTree/Visitor.h"      // for Visitor

namespace CodeGen {
	class GenType : public Visitor {
	  public:
		GenType( const std::string &typeString, bool pretty = false, bool genC = false, bool lineMarks = false );
		std::string get_typeString() const { return typeString; }
		void set_typeString( const std::string &newValue ) { typeString = newValue; }

		virtual void visit( FunctionType *funcType );
		virtual void visit( VoidType *voidType );
		virtual void visit( BasicType *basicType );
		virtual void visit( PointerType *pointerType );
		virtual void visit( ArrayType *arrayType );
		virtual void visit( ReferenceType *refType );
		virtual void visit( StructInstType *structInst );
		virtual void visit( UnionInstType *unionInst );
		virtual void visit( EnumInstType *enumInst );
		virtual void visit( TypeInstType *typeInst );
		virtual void visit( TupleType * tupleType );
		virtual void visit( VarArgsType *varArgsType );
		virtual void visit( ZeroType *zeroType );
		virtual void visit( OneType *oneType );

	  private:
		void handleQualifiers( Type *type );
		std::string handleGeneric( ReferenceToType * refType );
		void genArray( const Type::Qualifiers &qualifiers, Type *base, Expression *dimension, bool isVarLen, bool isStatic );

		std::string typeString;
		bool pretty = false; // pretty print
		bool genC = false;   // generating C code?
		bool lineMarks = false;
	};

	std::string genType( Type *type, const std::string &baseString, bool pretty, bool genC , bool lineMarks ) {
		GenType gt( baseString, pretty, genC, lineMarks );
		std::ostringstream os;

		if ( ! type->get_attributes().empty() ) {
			PassVisitor<CodeGenerator> cg( os, pretty, genC, lineMarks );
			cg.pass.genAttributes( type->get_attributes() );
		} // if

		type->accept( gt );
		return os.str() + gt.get_typeString();
	}

  std::string genPrettyType( Type * type, const std::string & baseString ) {
  	return genType( type, baseString, true, false );
  }

	GenType::GenType( const std::string &typeString, bool pretty, bool genC, bool lineMarks ) : typeString( typeString ), pretty( pretty ), genC( genC ), lineMarks( lineMarks ) {}

	void GenType::visit( VoidType *voidType ) {
		typeString = "void " + typeString;
		handleQualifiers( voidType );
	}

	void GenType::visit( BasicType *basicType ) {
		BasicType::Kind kind = basicType->get_kind();
		assert( 0 <= kind && kind < BasicType::NUMBER_OF_BASIC_TYPES );
		typeString = std::string( BasicType::typeNames[kind] ) + " " + typeString;
		handleQualifiers( basicType );
	}

	void GenType::genArray( const Type::Qualifiers &qualifiers, Type *base, Expression *dimension, bool isVarLen, bool isStatic ) {
		std::ostringstream os;
		if ( typeString != "" ) {
			if ( typeString[ 0 ] == '*' ) {
				os << "(" << typeString << ")";
			} else {
				os << typeString;
			} // if
		} // if
		os << "[";

		if ( isStatic ) {
			os << "static ";
		} // if
		if ( qualifiers.is_const ) {
			os << "const ";
		} // if
		if ( qualifiers.is_volatile ) {
			os << "volatile ";
		} // if
		if ( qualifiers.is_restrict ) {
			os << "__restrict ";
		} // if
		if ( qualifiers.is_atomic ) {
			os << "_Atomic ";
		} // if
		if ( dimension != 0 ) {
			PassVisitor<CodeGenerator> cg( os, pretty, genC, lineMarks );
			dimension->accept( cg );
		} else if ( isVarLen ) {
			// no dimension expression on a VLA means it came in with the * token
			os << "*";
		} // if
		os << "]";

		typeString = os.str();

		base->accept( *this );
	}

	void GenType::visit( PointerType *pointerType ) {
		assert( pointerType->get_base() != 0);
		if ( pointerType->get_isStatic() || pointerType->get_isVarLen() || pointerType->get_dimension() ) {
			genArray( pointerType->get_qualifiers(), pointerType->get_base(), pointerType->get_dimension(), pointerType->get_isVarLen(), pointerType->get_isStatic() );
		} else {
			handleQualifiers( pointerType );
			if ( typeString[ 0 ] == '?' ) {
				typeString = "* " + typeString;
			} else {
				typeString = "*" + typeString;
			} // if
			pointerType->get_base()->accept( *this );
		} // if
	}

	void GenType::visit( ArrayType *arrayType ) {
		genArray( arrayType->get_qualifiers(), arrayType->get_base(), arrayType->get_dimension(), arrayType->get_isVarLen(), arrayType->get_isStatic() );
	}

	void GenType::visit( ReferenceType *refType ) {
		assert( refType->get_base() != 0);
		assertf( ! genC, "Reference types should not reach code generation." );
		handleQualifiers( refType );
		typeString = "&" + typeString;
		refType->get_base()->accept( *this );
	}

	void GenType::visit( FunctionType *funcType ) {
		std::ostringstream os;

		if ( typeString != "" ) {
			if ( typeString[ 0 ] == '*' ) {
				os << "(" << typeString << ")";
			} else {
				os << typeString;
			} // if
		} // if

		/************* parameters ***************/

		const std::list<DeclarationWithType *> &pars = funcType->get_parameters();

		if ( pars.empty() ) {
			if ( funcType->get_isVarArgs() ) {
				os << "()";
			} else {
				os << "(void)";
			} // if
		} else {
			PassVisitor<CodeGenerator> cg( os, pretty, genC, lineMarks );
			os << "(" ;

			cg.pass.genCommaList( pars.begin(), pars.end() );

			if ( funcType->get_isVarArgs() ) {
				os << ", ...";
			} // if
			os << ")";
		} // if

		typeString = os.str();

		if ( funcType->get_returnVals().size() == 0 ) {
			typeString = "void " + typeString;
		} else {
			funcType->get_returnVals().front()->get_type()->accept( *this );
		} // if

		// add forall
		if( ! funcType->get_forall().empty() && ! genC ) {
			// assertf( ! genC, "Aggregate type parameters should not reach code generation." );
			std::ostringstream os;
			PassVisitor<CodeGenerator> cg( os, pretty, genC, lineMarks );
			os << "forall(";
			cg.pass.genCommaList( funcType->get_forall().begin(), funcType->get_forall().end() );
			os << ")" << std::endl;
			typeString = os.str() + typeString;
		}
	}

	std::string GenType::handleGeneric( ReferenceToType * refType ) {
		if ( ! refType->get_parameters().empty() ) {
			std::ostringstream os;
			PassVisitor<CodeGenerator> cg( os, pretty, genC, lineMarks );
			os << "(";
			cg.pass.genCommaList( refType->get_parameters().begin(), refType->get_parameters().end() );
			os << ") ";
			return os.str();
		}
		return "";
	}

	void GenType::visit( StructInstType *structInst )  {
		typeString = structInst->get_name() + handleGeneric( structInst ) + " " + typeString;
		if ( genC ) typeString = "struct " + typeString;
		handleQualifiers( structInst );
	}

	void GenType::visit( UnionInstType *unionInst ) {
		typeString = unionInst->get_name() + handleGeneric( unionInst ) + " " + typeString;
		if ( genC ) typeString = "union " + typeString;
		handleQualifiers( unionInst );
	}

	void GenType::visit( EnumInstType *enumInst ) {
		typeString = enumInst->get_name() + " " + typeString;
		if ( genC ) typeString = "enum " + typeString;
		handleQualifiers( enumInst );
	}

	void GenType::visit( TypeInstType *typeInst ) {
		typeString = typeInst->get_name() + " " + typeString;
		handleQualifiers( typeInst );
	}

	void GenType::visit( TupleType * tupleType ) {
		assertf( ! genC, "Tuple types should not reach code generation." );
		unsigned int i = 0;
		std::ostringstream os;
		os << "[";
		for ( Type * t : *tupleType ) {
			i++;
			os << genType( t, "", pretty, genC, lineMarks ) << (i == tupleType->size() ? "" : ", ");
		}
		os << "] ";
		typeString = os.str() + typeString;
	}

	void GenType::visit( VarArgsType *varArgsType ) {
		typeString = "__builtin_va_list " + typeString;
		handleQualifiers( varArgsType );
	}

	void GenType::visit( ZeroType *zeroType ) {
		// ideally these wouldn't hit codegen at all, but should be safe to make them ints
		typeString = (pretty ? "zero_t " : "long int ") + typeString;
		handleQualifiers( zeroType );
	}

	void GenType::visit( OneType *oneType ) {
		// ideally these wouldn't hit codegen at all, but should be safe to make them ints
		typeString = (pretty ? "one_t " : "long int ") + typeString;
		handleQualifiers( oneType );
	}

	void GenType::handleQualifiers( Type *type ) {
		if ( type->get_const() ) {
			typeString = "const " + typeString;
		} // if
		if ( type->get_volatile() ) {
			typeString = "volatile " + typeString;
		} // if
		if ( type->get_restrict() ) {
			typeString = "__restrict " + typeString;
		} // if
		if ( type->get_atomic() ) {
			typeString = "_Atomic " + typeString;
		} // if
		if ( type->get_lvalue() && ! genC ) {
			// when not generating C code, print lvalue for debugging.
			typeString = "lvalue " + typeString;
		}
	}
} // namespace CodeGen

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