/* $Id$ 
 * DotVisitor: create a .dot graph representation of the abstract syntax
 * tree.
 *
 * 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.
 */

#include <iostream>
#include <sstream>
#include <cassert>

#include "frontend/visitor/DotVisitor.hpp"
#include "frontend/ast/ConstInteger.hpp"
#include "frontend/ast/ConstReal.hpp"
#include "frontend/ast/Entity.hpp"
#include "frontend/ast/SymbolDeclaration.hpp"
#include "frontend/ast/ValDeclaration.hpp"
#include "frontend/ast/SignalDeclaration.hpp"
#include "frontend/ast/ConstantDeclaration.hpp"
#include "frontend/ast/Expression.hpp"
#include "frontend/ast/IfStat.hpp"
#include "frontend/ast/NullStat.hpp"
#include "frontend/ast/ForLoopStat.hpp"
#include "frontend/ast/WhileLoopStat.hpp"
#include "frontend/ast/NextStat.hpp"
#include "frontend/ast/VarAssignStat.hpp"
#include "frontend/ast/WaitStat.hpp"
#include "frontend/ast/ExitStat.hpp"
#include "frontend/ast/SigAssignStat.hpp"
#include "frontend/ast/WaveFormElem.hpp"
#include "frontend/ast/ReturnStat.hpp"
#include "frontend/ast/ProcCallStat.hpp"
#include "frontend/ast/AssertStat.hpp"
#include "frontend/ast/VarDeclaration.hpp"
#include "frontend/ast/DiscreteRange.hpp"
#include "frontend/ast/CaseStat.hpp"
#include "frontend/ast/CaseAlternative.hpp"
#include "frontend/ast/Others.hpp"
#include "frontend/ast/Architecture.hpp"
#include "frontend/ast/AssociationElement.hpp"
#include "frontend/ast/FunctionDeclaration.hpp"
#include "frontend/ast/ProcedureDeclaration.hpp"
#include "frontend/ast/CompInstStat.hpp"
#include "frontend/ast/Package.hpp"
#include "frontend/ast/PackageBody.hpp"
#include "frontend/ast/Process.hpp"
#include "frontend/ast/SubprogBody.hpp"
#include "frontend/ast/CondalSigAssign.hpp"
#include "frontend/ast/EnumerationType.hpp"
#include "frontend/ast/PhysicalType.hpp"
#include "frontend/ast/PhysicalTypeUnit.hpp"
#include "frontend/ast/RangeConstraintType.hpp"
#include "frontend/ast/UnconstrainedArrayType.hpp"
#include "frontend/ast/RecordType.hpp"
#include "frontend/ast/Aggregate.hpp"
#include "frontend/ast/FunctionCall.hpp"
#include "frontend/ast/ElementAssociation.hpp"
#include "frontend/ast/SubtypeIndication.hpp"
#include "frontend/ast/Library.hpp"
#include "frontend/ast/LibraryList.hpp"
#include "frontend/ast/Subscript.hpp"
#include "frontend/ast/Slice.hpp"
#include "frontend/ast/TypeConversion.hpp"
#include "frontend/ast/SimpleName.hpp"
#include "frontend/ast/AttributeName.hpp"
#include "frontend/ast/TemporaryName.hpp"
#include "frontend/ast/SelectedName.hpp"
#include "frontend/ast/ConstArray.hpp"
#include "frontend/ast/AttributeDeclaration.hpp"
#include "frontend/ast/AttributeSpecification.hpp"
#include "util/MiscUtil.hpp"

namespace ast {

DotVisitor::DotVisitor() : idCounter(0), currentDesc(NULL)
{
}

DotVisitor::~DotVisitor() 
{
	//free descritptions
	for (std::list<DotVisitor::NodeDescription*>::iterator i = 
			this->nodes.begin(); i != this->nodes.end(); i++) {
		delete *i;
	}
}

void
DotVisitor::visit(ElementAssociation& node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "Association");

	if (node.choices != NULL) {
		this->listTraverse(*node.choices);
		this->makeEdges(d->id, *node.choices, "choice");
	}
	
	if (node.actual != NULL) {
		node.actual->accept(*this);
		this->edges.push_back(
			Edge(d->id, node.actual->number, "actual"));
	}

	this->process(node);

}

void
DotVisitor::visit(ConstInteger& node)
{
	//FIXME
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "ConstInteger");
	
	d->add(util::MiscUtil::toString(node.value));
	this->process(node);
}

void
DotVisitor::visit(ConstReal& node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "ConstReal");

	d->add(util::MiscUtil::toString(node.value));
	this->process(node);
}

void
DotVisitor::visit(ConstArray &node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "ConstArray");

	this->process(node);

	this->listTraverse(*node.elements);
	this->makeEdges(d->id, *node.elements);
}

void
DotVisitor::visit(SimpleName& node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "SimpleName");
	
	assert(node.name != NULL);
	d->add(*node.name);

	std::string expandedPrefix = "";
	for (std::list<std::string*>::const_iterator i = 
		node.prefixStrings.begin(); i != node.prefixStrings.end();
		i++) {

		if (i != node.prefixStrings.begin()) {
			expandedPrefix += ".";
		}
		expandedPrefix += **i;
	}
	if (! expandedPrefix.empty()) {
		d->add("Scope", expandedPrefix);
	}
}

void
DotVisitor::visit(SelectedName& node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "SelectedName");
	
	assert(node.name != NULL);
	d->add(*node.name);
	this->process(node);
}

void
DotVisitor::visit(AttributeName& node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "AttributeName");
	
	assert(node.name != NULL);
	d->add(*node.name);
	this->process(node);
}

void
DotVisitor::visit(TemporaryName& node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "TemporaryName");
	
	d->add("(unnamed)");
	this->process(node);
}

void
DotVisitor::visit(Entity& node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "Entity");

	this->process(node);

	/* traverse ports */
	if (node.ports != NULL) {
		this->listTraverse(*node.ports);
		this->makeEdges(d->id, *node.ports, "ports");
	}


	/* traverse generics */
	if (node.generics != NULL) {
		this->listTraverse(*node.generics);
		this->makeEdges(d->id, *node.generics, "generics");
	}
}

void
DotVisitor::visit(LibraryList& node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "Libraries");

	this->process(node);

	this->listTraverse(node.libraries);
	this->makeEdges(d->id, node.libraries);
}

void
DotVisitor::visit(SignalDeclaration& node)
{
	this->makeDescription(node, "SignalDeclaration");
	this->process(node);
}

void
DotVisitor::visit(ConstantDeclaration& node)
{
	this->makeDescription(node, "ConstantDeclaration");
	this->process(node);
}

void
DotVisitor::visit(FunctionCall& node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "FunctionCall");

	this->process(node);

	if (node.subprog) {
		node.subprog->accept(*this);
		this->edges.push_back(Edge(d->id, node.subprog->number, 
						"name"));
	}

	if (node.arguments) {
		this->listTraverse(*node.arguments);
		this->makeEdges(d->id, *node.arguments, "arg");
	}
}

void
DotVisitor::visit(IfStat& node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "IF");

	this->process(node);

	if (node.thenStats) {
		this->listTraverse(*node.thenStats);
		this->makeEdges(d->id, *node.thenStats, "then");
	}

	if (node.elseStats) {
		this->listTraverse(*node.elseStats);
		this->makeEdges(d->id, *node.elseStats, "else");
	}
}

void
DotVisitor::visit(NullStat& node)
{
	this->makeDescription(node, "NULL");
	this->process(node);
}

void
DotVisitor::visit(ForLoopStat& node)
{

	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "FOR");

	this->process(node);

	if (node.loopVariable != NULL) {
		node.loopVariable->accept(*this);
		this->edges.push_back(
			Edge(d->id, node.loopVariable->number, "counter"));
	}
	
	/* traverse to discrete range */
	if (node.range != NULL) {
		node.range->accept(*this);
		this->edges.push_back(
				Edge(d->id, node.range->number, "range"));
	}
}

void
DotVisitor::visit(WhileLoopStat& node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "WHILE");
	
	this->process(node);

	/* traverse condition */
	if (node.condition) {
		node.condition->accept(*this);
		this->edges.push_back(
				Edge(d->id, node.condition->number, "cond"));
	}
}

void 
DotVisitor::visit(NextStat& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "NEXT");

	if (node.loopLabel) {
		d->add("label", *(node.loopLabel->name));
	}

	this->process(node);
}

void 
DotVisitor::visit(VarAssignStat& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, ":=");

	this->process(node);

	/* traverse to target */
	if (node.target) {
		node.target->accept(*this);
		this->edges.push_back(Edge(d->id, node.target->number));
	}
	
	/* traverse to source */
	if (node.source) {
		node.source->accept(*this);
		this->edges.push_back(Edge(d->id, node.source->number));
	}
}

void 
DotVisitor::visit(WaitStat& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "WAIT");

	this->process(node);

	/* traverse to Sensitivity list */
	if (node.sensitivities) {
		this->listTraverse(*node.sensitivities);
		this->makeEdges(d->id, *node.sensitivities, "on");
	}
	
	/* traverse to timeout */
	if (node.timeout) {
		node.timeout->accept(*this);
		this->edges.push_back(
				Edge(d->id, node.timeout->number, "until"));
	}
}

void 
DotVisitor::visit(ExitStat& node) 
{
	DotVisitor::NodeDescription *d = this->makeDescription(node, "EXIT");
	
	if (node.loopLabel) {
		d->add("Label:", *(node.loopLabel->name));
	}

	this->process(node);
}

void 
DotVisitor::visit(SigAssignStat& node) 
{
	DotVisitor::NodeDescription *d = this->makeDescription(node, "<=");

	this->process(node);

	/* traverse to target */
	if (node.target != NULL) {
		node.target->accept(*this);
		this->edges.push_back(
				Edge(d->id, node.target->number, "target"));
	}
	
	/* traverse to waveForm */
	if (node.waveForm != NULL) {
		this->listTraverse(*node.waveForm);
		this->makeEdges(d->id, *node.waveForm);
	}
}

void 
DotVisitor::visit(WaveFormElem& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "WaveFormElement");

	this->process(node);

	/* traverse to value */
	if (node.value != NULL) {
		node.value->accept(*this);
		this->edges.push_back(Edge(d->id, node.value->number));
	}
	
	/* traverse to delay */
	if (node.delay != NULL) {
		node.delay->accept(*this);
		this->edges.push_back(
				Edge(d->id, node.delay->number, "after"));
	}
}

void 
DotVisitor::visit(ReturnStat& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "RETURN");
	
	this->process(node);

	/* traverse to result */
	if (node.result != NULL) {
		node.result->accept(*this);
		this->edges.push_back(Edge(d->id, node.result->number));
	}
}

void 
DotVisitor::visit(ProcCallStat& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "ProcCall");

	this->process(node);

	/* traverse to referring symbol */
	if (node.subprog != NULL) {
		node.subprog->accept(*this);
		this->edges.push_back(Edge(d->id, node.subprog->number, 
						"name"));
	}

	/* traverse to arguments */
	if (node.arguments != NULL) {
		this->listTraverse(*node.arguments);
		this->makeEdges(d->id, *node.arguments);
	}
}

void 
DotVisitor::visit(AssertStat& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "ASSERT");
	
	this->process(node);

	/* traverse to report expression */
	if (node.report != NULL) {
		node.report->accept(*this);
		this->edges.push_back(
				Edge(d->id, node.report->number, "REPORT"));
	}

	/* traverse to severity expression */
	if (node.severity != NULL) {
		node.severity->accept(*this);
		this->edges.push_back(
				Edge(d->id, node.severity->number, "SEVERITY"));
	}
}

void 
DotVisitor::visit(VarDeclaration& node) 
{
	this->makeDescription(node, "VarDecl");
	this->process(node);
}

void 
DotVisitor::visit(DiscreteRange& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "DiscreteRange");

	switch(node.direction) {
	case DiscreteRange::DIRECTION_UP:
		d->add("TO");
		break;
	case DiscreteRange::DIRECTION_DOWN:
		d->add("DOWNTO");
		break;
	}

	this->process(node);

	/* traverse to from */
	if (node.from != NULL) {
		node.from->accept(*this);
		this->edges.push_back(Edge(d->id, node.from->number, "from"));
	}

	/* traverse to */
	if (node.to != NULL) {
		node.to->accept(*this);
		this->edges.push_back(Edge(d->id, node.to->number, "(down)to"));
	}

	if (node.rangeName != NULL) {
		node.rangeName->accept(*this);
		this->edges.push_back(
			Edge(d->id, node.rangeName->number, "RangebyName"));
	}
}


void 
DotVisitor::visit(CaseStat& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "CASE");

	this->process(node);

	/* traverse to select */
	if (node.select != NULL) {
		node.select->accept(*this);
		this->edges.push_back(
				Edge(d->id, node.select->number, "selector"));
	}

	/* traverse to alternatives */
	if (node.alternatives != NULL) {
		this->listTraverse(*node.alternatives);
		this->makeEdges(d->id, *node.alternatives);
	}
}

void 
DotVisitor::visit(CaseAlternative& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "CaseAlternative");
	
	this->process(node);

	/* traverse to isVals */
	if (node.isVals != NULL) {
		this->listTraverse(*node.isVals);
		this->makeEdges(d->id, *node.isVals, "is");
	}

	/* traverse to thenStats */
	if (node.thenStats != NULL) {
		this->listTraverse(*node.thenStats);
		this->makeEdges(d->id, *node.thenStats);
	}
}

void 
DotVisitor::visit(Others& node) 
{
	this->makeDescription(node, "OTHERS");
	this->process(node);
}

void 
DotVisitor::visit(Architecture& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "Architecture");

	this->process(node);

	/* traverse to concurrentStats */
	if (node.concurrentStats != NULL) {
		this->listTraverse(*node.concurrentStats);
		this->makeEdges(d->id, *node.concurrentStats);
	}
}

void 
DotVisitor::visit(AssociationElement& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "AssociationElement");

	this->process(node);

	/* traverse to formalPart */
	if (node.formal != NULL) {
		node.formal->accept(*this);
		this->edges.push_back(
			Edge(d->id, node.formal->number, "formal"));
	}

	/* traverse to actualPart */
	if (node.actual != NULL) {
		node.actual->accept(*this);
		this->edges.push_back(
			Edge(d->id, node.actual->number, "actual"));
	}
}

void 
DotVisitor::visit(FunctionDeclaration& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "FUNCTION");

	this->process(node);

	if (node.returnType != NULL) {
		node.returnType->accept(*this);
		this->edges.push_back(
			Edge(d->id, node.returnType->number, "returnT"));
	}

	if (node.isPure) {
		d->add("pure");
	}
}

void 
DotVisitor::visit(ProcedureDeclaration& node) 
{
	this->makeDescription(node, "PROCEDURE");
	this->process(node);
}

void 
DotVisitor::visit(CompInstStat& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "CompInstStat");
	this->process(node);

	/* traverse to entityName */
	if (node.entityName != NULL) {
		node.entityName->accept(*this);
		this->edges.push_back(Edge(d->id, node.entityName->number));
	}

	/* traverse to genericMap */
	if (node.genericMap != NULL) {
		this->listTraverse(*node.genericMap);
		this->makeEdges(d->id, *node.genericMap, "GENERIC MAP");
	}

	/* traverse to portMap */
	if (node.portMap != NULL) {
		this->listTraverse(*node.portMap);
		this->makeEdges(d->id, *node.portMap, "PORT MAP");
	}
}

void 
DotVisitor::visit(Package& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "PACKAGE");
	this->process(node);

	/* traverse to entityName */
	if (node.body != NULL) {
		node.body->accept(*this);
		this->edges.push_back(Edge(d->id, node.body->number));
	}

}

void 
DotVisitor::visit(PackageBody& node) 
{
	this->makeDescription(node, "PACKAGE BODY");
	this->process(node);
}

void 
DotVisitor::visit(Process& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "PROCESS");
	this->process(node);

	if (node.sensitivityList != NULL) {
		this->listTraverse(*node.sensitivityList);
		this->makeEdges(d->id, *node.sensitivityList, 
				"sensitivities");
	}

	if (node.declarations != NULL) {
		this->listTraverse(*node.declarations);
		this->makeEdges(d->id, *node.declarations, "decls");
	}

	if (node.seqStats != NULL) {
		this->listTraverse(*node.seqStats);
		this->makeEdges(d->id, *node.seqStats);
	}
}

void 
DotVisitor::visit(SubprogBody& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "SubprogBody");
	
	this->process(node);

	if (node.seqStats != NULL) {
		this->listTraverse(*node.seqStats);
		this->makeEdges(d->id, *node.seqStats);
	}

	if (node.declarations != NULL) {
		this->listTraverse(*node.declarations);
		this->makeEdges(d->id, *node.declarations);
	}
}

void 
DotVisitor::visit(CondalSigAssign& node) 
{

	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "<= (condal)");
	
	this->process(node);

	if (node.target != NULL) {
		node.target->accept(*this);
		this->edges.push_back(
				Edge(d->id, node.target->number, "target"));
	}

	if (node.assignStat != NULL) {
		node.assignStat->accept(*this);
		this->edges.push_back(Edge(d->id, node.assignStat->number));
	}
}

void 
DotVisitor::visit(EnumerationType& node) 
{
	this->makeDescription(node, "EnumerationType");
	this->process(node);
}

void 
DotVisitor::visit(PhysicalType& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "PhysicalType");

	this->process(node);

	if (node.constraint != NULL) {
		node.constraint->accept(*this);
		this->edges.push_back(Edge(d->id, node.constraint->number));
	}

	if (node.units != NULL) {
		this->listTraverse(*node.units);
		this->makeEdges(d->id, *node.units);
	}
}

void 
DotVisitor::visit(PhysicalTypeUnit& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "PhysicalTypeUnit");

	this->process(node);

	if (node.physUnit != NULL) {
		node.physUnit->accept(*this);
		this->edges.push_back(Edge(d->id, node.physUnit->number));
	}
}

void 
DotVisitor::visit(RangeConstraintType& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "RANGE");
	
	this->process(node);

	if (node.constraint != NULL) {
		node.constraint->accept(*this);
		this->edges.push_back(Edge(d->id, node.constraint->number));
	}
}

void 
DotVisitor::visit(UnconstrainedArrayType& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "ARRAY<>");
	
	this->process(node);

	if (node.indexTypes != NULL) {
		this->listTraverse(*node.indexTypes);
		this->makeEdges(d->id, *node.indexTypes);
	}

	//FIXME new members
}

void 
DotVisitor::visit(RecordType& node) 
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "RECORD");
	
	this->process(node);

	if (node.elements != NULL) {
		this->listTraverse(*(node.elements));
		this->makeEdges(d->id, *node.elements);
	}

}

void
DotVisitor::visit(RecordTypeElement& node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "RecordElement");

	this->process(node);

	if (node.subtype) {
		node.subtype->accept(*this);
		this->edges.push_back(Edge(d->id, node.subtype->number));
	}
}

void
DotVisitor::visit(Aggregate& node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "Aggregate");
	
	this->process(node);

	if (node.associations) {
		this->listTraverse(*node.associations);
		this->makeEdges(d->id, *node.associations);
	}
}

void
DotVisitor::visit(SubtypeIndication &node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "SubtypeIndication");

	this->process(node);

	if (node.typeName != NULL) {
		node.typeName->accept(*this);
		this->edges.push_back(Edge(d->id, node.typeName->number));
	}

	if (node.constraint != NULL) {
		node.constraint->accept(*this);
		this->edges.push_back(Edge(d->id, node.constraint->number));
	}

	if (node.indexConstraint != NULL) {
		this->listTraverse(*node.indexConstraint);
		this->makeEdges(d->id, *node.indexConstraint);
	}

	if (node.resolutionFunction != NULL) {
		node.resolutionFunction->accept(*this);
		this->edges.push_back(
			Edge(d->id, node.resolutionFunction->number));
	}
}

void
DotVisitor::visit(Library &node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "LibUnits");

	this->process(node);

	this->listTraverse(node.units);
	this->makeEdges(d->id, node.units);
}

void
DotVisitor::visit(Subscript &node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "Subscript");

	if (node.source != NULL) {
		node.source->accept(*this);
		this->edges.push_back(Edge(d->id, node.source->number));
	}

	if (node.indices != NULL) {
		this->listTraverse(*node.indices);
		this->makeEdges(d->id, *node.indices, "index");
	}
}

void
DotVisitor::visit(Slice &node)
{
	DotVisitor::NodeDescription *d = 
		this->makeDescription(node, "Slice");
	
	if (node.source != NULL) {
		node.source->accept(*this);
		this->edges.push_back(Edge(d->id, node.source->number));
	}

	if (node.range != NULL) {
		node.range->accept(*this);
		this->edges.push_back(
			Edge(d->id, node.range->number, "range"));
	}
}

void
DotVisitor::visit(TypeConversion &node)
{
	DotVisitor::NodeDescription* d =
		this->makeDescription(node, "TypeConversion");
	
	this->process(node);
	if (node.source != NULL) {
		node.source->accept(*this);
		this->edges.push_back(Edge(d->id, node.source->number));
	}

	//TODO target type name would be of interest.
}

void
DotVisitor::visit(AttributeDeclaration &node)
{
	this->makeDescription(node, "ATTRIBUTE");
	this->process(node);
}

void
DotVisitor::visit(AttributeSpecification &node)
{
	DotVisitor::NodeDescription* d =
		this->makeDescription(node, "AttributeSpec");
	this->process(node);

	assert(node.init != NULL);
	node.init->accept(*this);
	this->edges.push_back(Edge(d->id, node.init->number));

	/* FIXME edge to declaration? */
}


void
DotVisitor::process(ValDeclaration& node)
{
	DotVisitor::NodeDescription *d = this->currentDesc;

	switch (node.mode) {
	case ValDeclaration::MODE_IN:
		d->add("Mode", "in");
		break;
	case ValDeclaration::MODE_OUT:
		d->add("Mode", "out");
		break;
	case ValDeclaration::MODE_INOUT:
		d->add("Mode", "inout");
		break;
	}

	SymbolDeclaration& sNode = static_cast<SymbolDeclaration&>(node);
	this->process(sNode);


	/* traverse to initializer */
	if (node.init != NULL) {
		node.init->accept(*this);
		this->edges.push_back(
			Edge(d->id, node.init->number, "initializer"));
	}

	/* traverse to subtype indic */
	if (node.subtypeIndic != NULL) {
		node.subtypeIndic->accept(*this);
		this->edges.push_back(
			Edge(d->id, node.subtypeIndic->number, "subtype"));
	}
}

void
DotVisitor::process(SymbolDeclaration& node) 
{
	DotVisitor::NodeDescription *d = this->currentDesc;
	if (node.name) {
		d->add("Name", *node.name);
	}

	AstNode& aNode = static_cast<AstNode&>(node);
	this->process(aNode);
}

void
DotVisitor::process(Expression& node) 
{
	AstNode& aNode = static_cast<AstNode&>(node);
	this->process(aNode);
}

void
DotVisitor::process(SeqStat& node) 
{
	AstNode& anode = static_cast<AstNode&>(node);
	this->process(anode);
}

void
DotVisitor::process(LoopStat& node) 
{
	DotVisitor::NodeDescription *d = this->currentDesc;
	if (node.name) {
		d->add("Label", *node.name);
	}
	
	SeqStat& snode = static_cast<SeqStat&>(node);
	this->process(snode);

	/* taverse to loopStats */
	if (node.loopStats != NULL) {
		this->listTraverse(*node.loopStats);
		this->makeEdges(d->id, *node.loopStats);
	}
}

void
DotVisitor::process(ConditionedStat& node) 
{
	DotVisitor::NodeDescription *d = this->currentDesc;

	SeqStat& snode = static_cast<SeqStat&>(node);
	this->process(snode);

	/* taverse to condition */
	if (node.condition != NULL) {
		node.condition->accept(*this);
		this->edges.push_back(
				Edge(d->id, node.condition->number, "cond"));
	}
}

void
DotVisitor::process(Callable& node) 
{
	DotVisitor::NodeDescription *d = this->currentDesc;
	
	SymbolDeclaration& snode = static_cast<SymbolDeclaration&>(node);
	this->process(snode);

	/* taverse to arguments */
	if (node.arguments != NULL) {
		this->listTraverse(*node.arguments);
		this->makeEdges(d->id, *node.arguments);
	}

	if (node.definition != NULL) {
		node.definition->accept(*this);
		this->edges.push_back(
				Edge(d->id, node.definition->number, "body"));
	}
}

void
DotVisitor::process(LibUnit& node) 
{
	DotVisitor::NodeDescription *d = this->currentDesc;

	SymbolDeclaration& snode = static_cast<SymbolDeclaration&>(node);
	this->process(snode);

	if (node.libClauses) {
		this->listTraverse(*node.libClauses);
		this->makeEdges(d->id, *node.libClauses, "lib");
	}

	/* taverse to useClauses */
	if (node.useClauses != NULL) {
		this->listTraverse(*node.useClauses);
		this->makeEdges(d->id, *node.useClauses, "use");
	}

	if (node.declarations != NULL) {
		this->listTraverse(*node.declarations);
		this->makeEdges(d->id, *node.declarations, "decl");
	}
}

void
DotVisitor::process(TypeDeclaration& node) 
{
	SymbolDeclaration& snode = static_cast<SymbolDeclaration&>(node);
	this->process(snode);
}

void
DotVisitor::process(Name &node)
{
	//do nothing.
}

void
DotVisitor::process(PrefixedName &node)
{
	DotVisitor::NodeDescription *d = this->currentDesc;
	if (node.prefix != NULL) {
		node.prefix->accept(*this);
		this->edges.push_back(Edge(d->id,
					node.prefix->number, "prefix"));
	}
}


/* *************** END visiotor Methods ******************* */

DotVisitor::NodeDescription*
DotVisitor::makeDescription(AstNode& node, const char* desc)
{
	int id = this->assignId(node);
	DotVisitor::NodeDescription *d = 
		new DotVisitor::NodeDescription(id, desc);
	this->nodes.push_back(d);
	this->currentDesc = d;
	
	return d;
}

int
DotVisitor::assignId(AstNode& node) 
{
	this->idCounter++;
	node.number = this->idCounter;
	return this->idCounter;
}

void
DotVisitor::put(std::ostream& stream) const
{
	stream << "digraph AST {" << std::endl;

	for (std::list<NodeDescription*>::const_iterator i = 
		this->nodes.begin(); i != this->nodes.end(); i++) {
		(*i)->put(stream);
	}

	// put all edges
	for (std::list<Edge>::const_iterator i = 
		this->edges.begin(); i != this->edges.end(); i++) {
		i->put(stream);
	}

	stream << "}" << std::endl;
}


/* ***************** nested class Edge ******************** */

DotVisitor::Edge::~Edge() 
{
	delete this->label;
}

void
DotVisitor::Edge::put(std::ostream& stream) const
{
	stream << this->from << " -> " << this->to;
	if (this->label) {
		stream << "[label=\"" << *(this->label) << "\"]";
	}
	stream << ";" << std::endl;
}

/* ************* nested class NodeDescription ********** */
void
DotVisitor::NodeDescription::add(std::string n, std::string value)
{
	this->attributes.push_back("\\n" + n + ": " + value);
}

void
DotVisitor::NodeDescription::add(std::string n)
{
	this->attributes.push_back("\\n" + n);
}

void
DotVisitor::NodeDescription::put(std::ostream& stream) const
{
	stream << this->id << " [label=\"" << this->name;

	for (std::list<std::string>::const_iterator i = 
		this->attributes.begin(); i != this->attributes.end(); i++) {
		stream << *i;
	}

	stream << "\", shape=\"rect\"];" << std::endl;
}



}; /* namespace ast */
