/* $Id$ 
 *
 * NodeFactory: helper scripts for the Parser to create AST nodes, mainly
 *              useful to not keep lots of c++ code out of the parser.
 *
 * Copyright (C) 2007-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#ifndef __NODE_FACTORY_HPP_INCLUDED
#define __NODE_FACTORY_HPP_INCLUDED

#include <list>
#include <string>
#include "frontend/ast/Expression.hpp"
#include "frontend/ast/IfStat.hpp"
#include "frontend/ast/LoopStat.hpp"
#include "frontend/ast/ValDeclaration.hpp"
#include "frontend/ast/CondalSigAssign.hpp"
#include "frontend/ast/Location.hpp"
#include "frontend/ast/RecordType.hpp"
#include "frontend/reporting/SyntaxError.hpp"
#include "frontend/visitor/TopDownVisitor.hpp"
#include "frontend/ast/SignalDeclaration.hpp"
#include "frontend/ast/ConstantDeclaration.hpp"
#include "frontend/ast/Aggregate.hpp"
#include "frontend/ast/SubtypeIndication.hpp"

namespace ast {

class Symbol;
class SymbolTable;
class NameLookup;

/** The NodeFactory creates various AST nodes. It serves as a bridge between
 *  the parser and the Syntax Tree.
 */
class NodeFactory {
public:
	/** entity class of an attribute specification */
	enum entityClassE {
		EC_ENTITY,
		EC_ARCHITECTURE,
		EC_CONFIGURATION,
		EC_PROCEDURE,
		EC_FUNCTION,
		EC_PACKAGE,
		EC_TYPE,
		EC_SUBTYPE,
		EC_CONSTANT,
		EC_SIGNAL,
		EC_VARIABLE,
		EC_COMPONENT,
		EC_LABEL,
		EC_LITERAL,
		EC_UNITS,
		EC_GROUP,
		EC_FILE
	};

	/** storage helper class to unify range and index constraints. */
	class Constraint {
	public:
		/** range constraint (if any) */
		DiscreteRange *rangeConstraint;
		/** index constraint(s) (if any) */
		std::list<DiscreteRange*> *indexConstraint;
	};
	/** glue class to forward information about one identifier */
	class Identifier {
	public:
		//! c'tor
		/** @param id string of the identifier
		 *  @param sym corresponding symbol (if any)
		 */
		Identifier(
			std::string *id, 
			std::list<Symbol*> cands
			) : 	identifier(id), candidates(cands) {}
		
		/** string of the identifier */
		std::string *identifier;
		/** list of candidate symbols */
		std::list<Symbol*> candidates;
	};

	/** helper class for generating if_stats and condal signal
	 *  assignments. 
	 *  */
	struct IfHelperS {
		/** dummy c'tor: initialize members with NULL */
		IfHelperS() : topstat(NULL), bottomElse(NULL) {}
		/** topmost if-statement of elsif part */
		IfStat *topstat;
		/** reference to bottom most else part of elsif */
		std::list<SeqStat*> **bottomElse;
	};

	/** helper struct to flatten context items during parsing. */
	struct ContextItemS {
		/** dummy c'tor: initialize members with NULL */
		ContextItemS() : libClauses(NULL), useClauses(NULL) {}
		/** library clauses */
		std::list<SimpleName*> *libClauses;
		/** use clauses */
		std::list<Name*> *useClauses;
	};

	/** helper struct for type definitions. */
	struct TypeDeclHelper {
	public:
		/** c'tor for enumeration types
		 *  @param et declared EnumerationType */
		TypeDeclHelper(
			EnumerationType *et
			) : 	enumType(et), 
				typeDecl(NULL),
				conArrayBase(NULL),
				wasRecordType(false) {}
		/** c'tor for record types.
		 *  @param rt declared RecordType
		 */
		TypeDeclHelper(
			RecordType *rt
			) :	enumType(NULL),
				typeDecl(rt),
				conArrayBase(NULL),
				wasRecordType(true) {}

		/** c'tor for constraint array types.
		 *  The constraint array types declare an anonymous 
		 *  unconstraint array and a subtype with an index constraint.
		 *  @param indexConstraint index constraint of the array.
		 *  @param elementType SubtypeIndication of the elements.
		 *  @param loc location of the array definition.
		 *  @param s SymbolTable instance.
		 */
		TypeDeclHelper(
			std::list<DiscreteRange*> *indexConstraint,
			SubtypeIndication *elementType,
			Location loc,
			SymbolTable &s);

		/** c'tor for other types.
		 *  @param other any other type declaration.
		 */
		TypeDeclHelper(
			TypeDeclaration *other
			) :	enumType(NULL),
				typeDecl(other),
				conArrayBase(NULL),
				wasRecordType(false) {}

		/** register type declaration to SymbolTable
		 *  @param symTab SymbolTable instance.
		 *  @param id identifier of the type declaration.
		 */
		void 
		registerType(
			SymbolTable &symTab,
			NodeFactory::Identifier *id
		) const;

		/** flatten to a list of SymbolDeclarations. */
		std::list<SymbolDeclaration*>*
		flatten(void) const;

		/** enumeration type, in case one was declared. */
		EnumerationType *enumType;
		/** any other type declaration. */
		TypeDeclaration *typeDecl;
		/** corresponding anonymous base type of a constraint array */
		TypeDeclaration *conArrayBase;
		/** was this type declaration a record type? */
		bool wasRecordType;
	};

	/** Helper function for parsing elsif statement lists
          * Attach a new ifStat from condition, thenStats to remainder.
          * If remainder doesn't exist, create a new IfHelperS.
          *
          * @param remainder the parent elsif remainder.
          * @param condition condition of the current elsif part.
          * @param thenStats then-statements of the current elsif part.
	  * @param loc location of the elseif token.
          *
          * @return adjusted remainder or a new one if remainder doesn't exist
          *         yet.
          */
	static struct IfHelperS& 
	createNestedElseIfs(
		struct IfHelperS *remainder,
		Expression* condition,
		std::list<SeqStat*> *thenStats,
		Location loc
	);

	/** Helper function for parsing if_stats.
          * In case of elseIfs present, attach elseStats to bottom most
          * if-statement, and set elsIf part of current if statement to
          * else-part.
          * Otherwise build a "regular" if-then-else statement.
          * 
          * @param condition condition of the current if-stat.
          * @param thenStats then-statement of the current if-stat.
          * @param elsIfs the elseIfs, which should already be nested. Struct
          *        will get deleted from this function.
          * @param elseStats else statements of current if-stat.
	  * @param loc location of the if token.
          * 
          * @return parsed IfStat.
          */
	static struct IfStat& 
	createIfStat(
		Expression *condition,
		std::list<SeqStat*> *thenStats,
		struct IfHelperS* elsIfs,
		std::list<SeqStat*> *elseStats,
		Location loc
	);

	
	/** Create a list of Signal declarations.
          * The contents of the parameters are reused in the result, so
          * the caller mustn't free them.
          *
          * @param ids list of Identifiers for each Signal. The list itself 
          *            isn't reused anywhere, so caller can free it.
          * @param mode access mode of signal (in, out, inout).
          * @param isBus is the signal tagged as bus?
          * @param varInit optional initializer of signal.
          * @param subtypeIndic subtype indication of signal.
	  * @param loc location of the signal declaration.
          * @return list of ValDecls, caller must free it.
          */
	static std::list<SignalDeclaration*>& 
	makeSignalDecls(
		std::list<std::string*> &ids,
		enum ValDeclaration::Mode mode, 
		bool isBus, 
		Expression *varInit,
		SubtypeIndication *subtypeIndic,
		Location loc
	);


	/** Create a list of constant declarations.
          * The contents of the parameters are reused in the result, so
          * the caller mustn't free them.
          *
          * @param ids list of Identifiers for each Signal. The list itself 
          *            isn't reused anywhere, so caller can free it.
          * @param varInit optional initializer of constant.
          * @param subtypeIndic subtype indication of the constant.
	  * @param loc Location of the constant declaration.
          * @return list of ValDecls, caller must free it.
          */
	static std::list<ConstantDeclaration*>& 
	makeConstantDecls(
		std::list<std::string*> &ids,
		Expression* varInit,
		SubtypeIndication *subtypeIndic,
		Location loc
	);

	/** Create a list of Variable declarations.
          * The contents of the parameters are reused in the result, so
          * the caller mustn't free them.
          *
          * @param ids list of Identifiers for each Variable. The list itself
          *            isn't reused anywhere, so caller can free it.
          * @param varInit optional initializer of variable.
          * @param subtypeIndic subtype indication of the variable.
	  * @param loc Location of the variable declaration.
          * @return list of ValDecls, caller must free it.
          */
	static std::list<SymbolDeclaration*>& 
	makeVarDecls(
		std::list<std::string*> &ids,
		Expression *varInit,
		SubtypeIndication *subtypeIndic,
		Location loc
	);


	/** Create a list of ValDeclaration declarations.
          * The contents of the parameters are reused in the result, so
          * the caller mustn't free them.
          *
          * This version is for function declarations.
          *
          * @param ids list of Identifiers for each Variable. The list itself
          *            isn't reused anywhere, so caller can free it.
          * @param varInit optional initializer of variable.
          * @param cls object class that will determine if it's a 
          * 	   Signal, Variable or Constant.
          * @param subtypeIndic subtype indication of the element.
	  * @param mode access mode of the ValDeclaration.
	  * @param loc location in the source file.
          * @return list of ValDecls, caller must free it.
          */
	static std::list<ValDeclaration*>&
	makeFuncInterfaceDecls(
		std::list<std::string*> &ids,
		Expression *varInit,
		enum ValDeclaration::Mode mode,
		enum ValDeclaration::ObjClass cls,
		SubtypeIndication *subtypeIndic,
		Location loc
	);

	/** Create a list of ValDeclaration declarations.
          * The contents of the parameters are reused in the result, so
          * the caller mustn't free them.
          *
          * This version is for procedure declarations.
          *
          * @param ids list of Identifiers for each Variable. The list itself
          *            isn't reused anywhere, so caller can free it.
          * @param varInit optional initializer of variable.
          * @param mode access mode (in/out/inout)
          * @param cls object class that will determine if it's a 
          * 	   Signal, Variable or Constant.
          * @param subtypeIndic subtype indication of the element.
	  * @param loc location in the source file.
          * @return list of ValDecls, caller must free it.
          */
	static std::list<ValDeclaration*>&
	makeProcInterfaceDecls(
		std::list<std::string*> &ids,
		Expression *varInit,
		enum ValDeclaration::Mode mode,
		enum ValDeclaration::ObjClass cls,
		SubtypeIndication *subtypeIndic,
		Location loc
	);

	/** merge two context items.
          * The order will be all items from first context item first, then
          * items from second context item.
          *
          * @param first first context item. Parameter will be reused for
          *        result, caller must free it.
          * @param second second context item. Parameter will get freed
          *        during call.
          * @result merged context item.
          */
	static struct ContextItemS&
	mergeContextItems(
		struct ContextItemS &first, 
		struct ContextItemS &second
	);

	/** correctly create a CondalSigAssign during parsing.
	 *  @param trg target value. Reused in result.
	 *  @param conds top IfStat or SigAssignStat. Reused in result.
	 *  @param loc location of assignment token.
	 *  @return created CondalSigAssign. Allocated node, caller should
	 *          free it once done with it.
	 */
	static CondalSigAssign&
	makeCondalSigAssign(Name &trg, SeqStat &conds, Location loc);

	/** create a list of record type elements.
          * @param idlist list of identifiers.
          * @param subtype subtype indication of element.
          * @param loc location of the first elements (FIXME)
          * @return list of elements.
          */
	static std::list<RecordTypeElement*>&
	makeRecordTypeElements(
		std::list<std::string*> &idlist, 
		SubtypeIndication &subtype,
		Location loc);

	/** create the corresponding Aggregate from the give String Literal.
	 *  @param stringLit string literal.
	 *  @param loc Location of the string literal.
	 *  @param nl NameLookup instance.
	 *  @return the corresponding Aggregate or NULL if the string is a
	 *          null-string.
	 */
	static Aggregate* 
	makeAggregateFromString(
		const std::string &stringLit, 
		Location loc,
		const NameLookup &nl
	);

	//! register all enumeration elements from an enumeration type
	/** @param etype EnumerationType for which all EnumerationElements
	 *         should get registered in the SymbolTable
	 *  @param symbolTable SymbolTable instance.
	 *  @param typeSym Symbol of the EnumerationType.
	 */
	static void
	registerEnumElems(
		const EnumerationType *etype,
		SymbolTable &symbolTable,
		Symbol *typeSym
	);

	/** create an AttributeSpecification for the attribute denoted by
	 *  designator with initializer init and attribute the named 
	 *  entities in entityList that fit the entity class classFilter 
	 *  with it.
	 *  @param designator referred to attribute
	 *  @param entityList list of named entities to attribute
	 *  @param classFilter entity class
	 *  @param init initializer for the specification.
	 *  @param symTab SymbolTable instance.
	 */
	static AttributeSpecification *
	handleAttributeSpecs(
		SimpleName *designator,
		std::list<SimpleName*> entityList,
		enum entityClassE classFilter,
		Expression *init,
		const SymbolTable &symTab);

	/** safe upward cast a list of derived class U to a list of parent
          * class T.
          * @param l pointer to list of derived class pointers. Memory will
          *        be reused as base class list.
          * @return list of base class pointers, type T
          *
          * Hint: if this function won't compile anywhere, just copy the
          * elements from the list, as this will always work (but is 
          * slower).
          */
	template <typename T, typename U>
	static std::list<T>*
	listCast(std::list<U> *l);

	/** combine two lists of same type.
          * @param dest list where elements are getting push_back'ed.
          * @param source list which contains elements to add.
          */
	template <typename T>
	static void
	listCombine(std::list<T> *dest, std::list<T> *source);

private:
	
	/** is the symbol sym of entityClass ec?
	 *  @param sym Symbol to check.
	 *  @param ec entity class
	 *  @return true if sym is of class ec.
	 */
	static bool
	isOfEntityClass(const Symbol &sym, enum entityClassE ec);
	
	//! very small visitor for attributing CondalSigAssign stats.
	/** This visitor will set the target for each SigAssignStat
	 *  that's part of a CondalSigAssign.
	 */
	class CondalSigAssignSetter : public TopDownVisitor {
	public:
		//! c'tor
		/** @param trg target that will get passed downwards.
		 */
		CondalSigAssignSetter(Name &trg) : target(trg) {}
		
		/** set target to SigAssignStat nodes.
		 *  @param node SigAssignStat that gets visited.
		 */
		virtual void visit(SigAssignStat &node);
		
	private:
		/** target that will be passed downwards. */
		Name &target;
	};

};

}; /* namespace ast */

/* template definitions */
#include "frontend/ast/NodeFactory.tpp"

#endif /* __NODE_FACTORY_HPP_INCLUDED */
