/* $Id$ 
 * NameLookup: Knows about VHDL names and how/where to look these up in the
 * symboltable (i.e. it can e.g. switch the scope for prefixed names etc.)
 *
 * Copyright (C) 2008-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 __NAME_LOOKUP_HPP_INCLUDED
#define __NAME_LOOKUP_HPP_INCLUDED

#include <list>
#include "frontend/misc/SymbolTable.hpp"
#include "frontend/ast/SimpleName.hpp"
#include "frontend/ast/Callable.hpp"
#include "frontend/ast/NodeFactory.hpp"
#include "frontend/reporting/CompileError.hpp"

namespace ast {

//! Glue class for SymbolTable while parsing.
/** This class is a glue class between SymbolTable and the ParserDriver
 *  used only for parsing.
 */
class NameLookup {
public:
	//! c'tor
	/** @param symtab SymbolTable instance.
	 */
	NameLookup(SymbolTable &symtab);

	//! open the region associated with the symbol corresponding to name
	/** wrapper for symbol table to open the region associated with
	 *  the symbol name. Will report an error if the symbol doesn't have
	 *  an associated DeclarativeRegion. Will also report an error if the
	 *  name is not a SimpleName.
	 *
	 *  @param name (Simple)Name with an associated Region.
	 */
	void openRegion(Name *name) /* throw(CompileError) */;

	//! shift the current lookup scope
	/** Shift the current lookup scope to the scope defined by prefix.
	 *  This is used to correctly look up the suffices of prefixed names.
	 *  This will only effect the very next symbol lookup via lookup.
	 *  @param prefix which refers to the current scope.
	 *  @param isFunction denotes the prefix a function name?
	 */
	void
	shiftScope(Name &prefix, bool isFunction);

	//! shift the current lookup scope to denote an attribute prefix.
	/** This function shifts the lookup scope to denote an attribute
	 *  prefix. The following lookups will return attribute names.
	 *  Reset with unshiftScope.
	 */
	void shiftAttributeScope(void);

	//! find or register the callable spec.
	/** Resolve the parameter (type) list of spec and look if there
	 *  is a conforming subprogram specification registered in the 
	 *  SymbolTable already. If so, open its region, delete spec and 
	 *  return the specification. If not, register spec and open its
	 *  region, and register the parameters.
	 *
	 *  @param spec subprogram specification.
	 *  @return either the conformin specification or spec.
	 */
	Callable* findOrRegisterSubprog(Callable *spec);

	//! lookup the identifier id in the symbol table.
	/** @param id identifier to lookup.
	 *  @return list of candidate symbols (might be empty if not 
	 *          found).
	 */
	std::list<Symbol*> lookup(std::string &id) const;

	//! force to reset any scope lookups for expanded/selected names.
	/** Force undo any effect of shiftScope.
	 */
	void unshiftScope(void);

	//! generate either a selected or an expanded name
	/** @param prefix prefix of the selected or expanded name
	 *  @param suffix suffix of the selected or expanded name
	 *  @param candidates candidate symbols of the suffix
	 *  @param loc location of the suffix.
	 *  @return corresponding expanded or selected name
	 */
	Name* makeSelectedName(
		Expression *prefix, 
		std::string *suffix, 
		std::list<Symbol*> candidates,
		Location loc) const;

	//! register lib clauses in symboltable.
	/** @param libs list of library clauses
	 */
	void registerLibClauses(const std::list<SimpleName*> &libs);

	//! register use clauses in symboltable.
	/** @param usecs list of use clauses.
	 */
	void registerUseClauses(const std::list<Name*> &usecs);

	//! shift candidates by a subsription
	/** Shift the candidates cands by a subscription and return all
	 *  valid candidates. Also remove non-fitting candidates from 
	 *  cands.
	 *
	 *  @param cands prefix candidates
	 *  @param loc Location of the subscription (for error reporting only)
	 *  @return all possible shifted candidates
	 */
	static std::list<Symbol*> 
	shiftSubscriptCands(std::list<Symbol*> &cands, Location loc);

	//! shift scope for a selected name
	/** shift scope for a selected name
	 *
	 *  @param prefix prefix of the name.
	 */
	void shiftSelectedScope(Name &prefix);

	/** is the current resolved symbol an expanded name? 
	 */
	bool isExpanded;

private:
	/** shift one candidate by a subscription.
	 *  @param candidate prefix candidate
	 *  @return shifted symbol or NULL if subscription is not possible.
	 */
	static Symbol*
	shiftSubscriptCand(Symbol *candidate);

	//! shift scope for one symbol of a selected name
	/** shift scope for one candidate symbol of a selected name.
	 *
	 *  @param prefixSym canidate symbol
	 */
	void shiftSelectedSym(Symbol *prefixSym);

	//! shift current lookup scope to an expanded name.
	/** @param sym symbol with a declarative region that gets
	 *         interpreted as an expanded name.
	 */
	void shiftExpanded(const Symbol &sym);

	//! symbol table instance.
	SymbolTable &symbolTable;

	/** list of possible scopes for which symbols should get looked up.
	 *  For expanded names which are not overloadable, this is the scope
	 *  of the expanded name prefix, for overloadable names it can
	 *  be more than one scope. For normal identifiers, this list should
	 *  be empty.
	 */
	std::list<DeclarativeRegion*> lookupScopes;

	/** Check two subprogram specifications that are procedure 
	 *  specifications for conformance.
	 *  @param a first subprogram specification
	 *  @param b second subprogram specification
	 *  @return true if both conform.
	 */
	static bool
	conformProcSig(const Callable *a, const Callable *b);

	/** Check two FunctionDeclarations for conformance.
	 *  @param a first FunctionDeclaration
	 *  @param b second FunctionDeclaration
	 *  @return true if they conform, false otherwise.
	 */
	static bool 
	conformFuncSig(
		const FunctionDeclaration *a,
		const FunctionDeclaration *b);

	/** check two ValDeclarations as part of subprogram specifications for
	 *  conformance.
	 *
	 *  @param a first ValDeclaration 
	 *  @param b second ValDeclaration
	 *  @return true if they conform.
	 */
	static bool 
	conformParameter(const ValDeclaration *a, const ValDeclaration *b);

	/** was the scope shifted, and should hence shifted lookups
	 *  instead of regular symbol table lookups be performed?
	 */
	bool shiftedLookups;
};

}; /* namespace ast */

#endif /* __NAME_LOOKUP_HPP_INCLUDED */
