//
// 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.
//
// InitModel.h -- 
//
// Author           : Rodolfo G. Esteves
// Created On       : Mon May 18 07:44:20 2015
// Last Modified By : Peter A. Buhr
// Last Modified On : Tue May 19 16:37:52 2015
// Update Count     : 2
//

#ifndef _INITTWEAK_MODEL_H_
#define _INITTWEAK_MODEL_H_

#include "Association.h"
#include "Common/SemanticError.h"
#include "SynTree/Visitor.h"

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

namespace InitTweak {
	class InitModelBuilder : public AssociationBuilder, public Visitor {
	  public:
		InitModelBuilder( Declaration * );
		~InitModelBuilder();

		virtual Association *grab_assoc() { taken = true; return building; }
		virtual Association *get_assoc() { return building; }
		void set_assoc( Association *newAssoc ) { building = newAssoc; }

		void init();
		static int interpretDimension( Expression *exp ) {
			ConstantFolder folder( exp );
			try {
				return folder.get_constant();
			} catch (...) {
				throw SemanticError("Invalid array dimension");
			}
		}

		// types
		virtual void visit( ArrayType * );
		virtual void visit( StructInstType * );
		virtual void visit( UnionInstType * );
		virtual void visit( EnumInstType * );
		virtual void visit( ContextInstType * ) { throw 0; }
		virtual void visit( TypeInstType * )    { throw 0; }
		// virtual void visit( TupleType *tupleType );
		// declarations
		virtual void visit( StructDecl *);
		virtual void visit( UnionDecl *);
		virtual void visit( EnumDecl *);
	  private:
		class ConstantFolder : public Visitor {
		  public:
			ConstantFolder( Expression *_expr = 0 ): expr(_expr) {}
			int get_constant() throw() { expr->accept( *this ); return value; }
			void set_constant( Expression *newExp ) { expr = newExp; }
			// Visitor interface
			void visit( Expression * ) { throw 0; }
			void visit( NameExpr * ) { throw 0; }
			void visit( CastExpr * ) { throw 0; }
			void visit( UntypedMemberExpr * ) { throw 0; }
			void visit( VariableExpr * ) { throw 0; }
			void visit( ConstantExpr * );
			void visit( SizeofExpr * ) { throw 0; }
			void visit( AlignofExpr * ) { throw 0; }
			void visit( UntypedOffsetofExpr * ) { throw 0; }
			void visit( OffsetofExpr * ) { throw 0; }
			void visit( OffsetPackExpr * ) { throw 0; }
			void visit( AttrExpr * ) { throw 0; }
			void visit( LogicalExpr * ) { throw 0; }
			void visit( ConditionalExpr * ) { throw 0; }
			void visit( CommaExpr * ) { throw 0; }
		  private:
			Expression *expr;
			int value;
		};

		bool taken;
		Declaration *decl;  // ?
		Association *building;
	};

	class InitModelFiller : public AssociationFiller, public Visitor {
	  public:
		InitModelFiller( Association *, Initializer *, bool _topLevel = false );
		~InitModelFiller() { /* pointers in here are not owned by object (never created by object either) */ }
		virtual Association *get_assoc() { return model; }
		virtual void set_assoc( Association *newAssoc ) { model = newAssoc; }

		void init();
		// Visitor interface
		virtual void visit( SingleInit *singleInit );
		virtual void visit( ListInit *listInit );
	  private:
		Association *model;
		Initializer *orgInit;
		bool topLevel;
		long int next;
	};

	class InitUnspooler : public AssociationVisitor {
	  public:
		InitUnspooler() : init(0), taken( false ) {}
		virtual ~InitUnspooler() { if ( ! taken && (init != 0)) { delete init; init = 0; } }
		Initializer *get_initializer() { return init; }
		Initializer *grab_initializer() { taken = true; return init; }

		virtual void visit( SingleName * );
		virtual void visit( PointAssociation * );
		virtual void visit( RangeAssociation * ) { std::cerr << "InitUnspooler - In a range assoc" << std::endl; return; }
	  private:
		Initializer *init;
		bool taken;
	};
} // namespace InitTweak

#endif // _INITTWEAK_MODEL_H_

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