//
// 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 : Thu Jul  9 16:43:52 2015
// Update Count     : 13
//

#include <sstream>
#include <cassert>

#include "GenType.h"
#include "CodeGenerator.h"

#include "SynTree/Declaration.h"
#include "SynTree/Expression.h"
#include "SynTree/Type.h"
#include "SynTree/Visitor.h"

namespace CodeGen {
	class GenType : public Visitor {
	  public:
		GenType( const std::string &typeString );
		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( StructInstType *structInst );
		virtual void visit( UnionInstType *unionInst );
		virtual void visit( EnumInstType *enumInst );
		virtual void visit( TypeInstType *typeInst );
		virtual void visit( VarArgsType *varArgsType );
  
	  private:
		void handleQualifiers( Type *type );
		void genArray( const Type::Qualifiers &qualifiers, Type *base, Expression *dimension, bool isVarLen, bool isStatic );
  
		std::string typeString;
	};

	std::string genType( Type *type, const std::string &baseString ) {
		GenType gt( baseString );
		type->accept( gt );
		return gt.get_typeString();
	}

	GenType::GenType( const std::string &typeString ) : typeString( typeString ) {}

	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.isConst ) {
			os << "const ";
		} // if
		if ( qualifiers.isVolatile ) {
			os << "volatile ";
		} // if
		if ( qualifiers.isRestrict ) {
			os << "__restrict ";
		} // if
		if ( qualifiers.isAtomic ) {
			os << "_Atomic ";
		} // if
		if ( qualifiers.isAttribute ) {
			os << "__attribute(( )) ";
		} // if
		if ( dimension != 0 ) {
			CodeGenerator cg( os );
			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( 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 {
			CodeGenerator cg( os );
			os << "(" ;

			cg.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
	}

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

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

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

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

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

	void GenType::handleQualifiers( Type *type ) {
		if ( type->get_isConst() ) {
			typeString = "const " + typeString;
		} // if
		if ( type->get_isVolatile() ) {
			typeString = "volatile " + typeString;
		} // if
		if ( type->get_isRestrict() ) {
			typeString = "__restrict " + typeString;
		} // if
		if ( type->get_isAtomic() ) {
			typeString = "_Atomic " + typeString;
		} // if
		if ( type->get_isAttribute() ) {
			typeString = "__attribute(( )) " + typeString;
		} // if
	}
} // namespace CodeGen

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