/* $Id$
 *
 * Lookup Symbols (currently only References) and annotate them with the
 * corresponding Data nodes.
 *
 * Copyright (C) 2010 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.
 */

#include <algorithm>
#include "intermediate/visitor/LookupSymbols.hpp"
#include "intermediate/operands/Reference.hpp"
#include "intermediate/container/Data.hpp"
#include "intermediate/container/CodeContainer.hpp"
#include "intermediate/opcodes/SetParam.hpp"
#include "intermediate/opcodes/Call.hpp"
#include "intermediate/opcodes/Proc.hpp"

namespace intermediate {

void
LookupSymbols::visit(CodeContainer &node)
{
	this->currentContainer = &node;
	this->listTraverse(node.typeDefinitions);
	this->listTraverse(node.transferData);
	this->listTraverse(node.stackData);
	this->listTraverse(node.children);
	this->currentContainer = &node;
	this->listTraverse(node.code);
}


void
LookupSymbols::visit(Reference &node)
{
	const Data *d = this->findSymbol(node.name);
	node.associatedData = d;
}

void
LookupSymbols::visit(SetParam &node)
{
	node.src->accept(*this);
	// don't traverse to dst (yet?). It's located in the
	// transfer segment of a container on the same level
	// as the currently processed container.
	// Lookup would need to be adjusted for it.
}

void
LookupSymbols::visit(Call &node)
{
	assert(node.dst != NULL);
	node.dst->associatedContainer = this->findCallee(node.dst->name);
}

void
LookupSymbols::visit(Proc &node)
{
	assert(node.dst != NULL);
	node.dst->associatedContainer = this->findCallee(node.dst->name);
}

const CodeContainer *
LookupSymbols::findCallee(const std::string &name) const
{
	// Callee can be a direct child, or anywhere in the surrounding
	// containers.
	const CodeContainer *c = this->currentContainer;
	while (c != NULL) {
		for (std::list<CodeContainer*>::const_iterator i = 
			c->children.begin();
			i != c->children.end(); i++) {

			if ((*i)->name == name) {
				return *i;
			}
		}

		if (c->name == name) {
			return c;
		}

		c = c->staticParent;
	}

	return NULL;
}


Data *
LookupSymbols::findSymbol(const std::string &name) const
{
	const CodeContainer *c = this->currentContainer;
	const CompareData cd = CompareData(name);

	while (c != NULL) {
		std::list<Data *>::const_iterator needle = 
				std::find_if(
					c->stackData.begin(),
					c->stackData.end(),
					cd);
		if (needle != c->stackData.end()) {
			return *needle;
		}

		needle = std::find_if(
				c->transferData.begin(),
				c->transferData.end(),
					cd);
		if (needle != c->transferData.end()) {
			return *needle;
		}

		c = c->staticParent;
	}
	return NULL;
}

/* ------------------- Nested class: CompareData ------------------- */
bool
LookupSymbols::CompareData::operator ()(const Data *d) const
{
	if (d == NULL) {
		return false;
	}
	return d->name == this->name;
}

}; /* namespace intermediate */
