#include <string>
#include <list>
#include <iterator>
#include <algorithm>
#include <cassert>

#include "ParseNode.h"
#include "TypeData.h"
#include "utility.h"
#include "SynTree/Declaration.h"
#include "SynTree/Expression.h"
#include "SynTree/Initializer.h"
#include "SemanticError.h"
#include "UniqueName.h"
#include "LinkageSpec.h"

using namespace std;

/* these must remain in the same order as the corresponding DeclarationNode enumerations */
const char *DeclarationNode::qualifierName[] = { "const", "restrict", "volatile", "lvalue" };
const char *DeclarationNode::basicTypeName[] = { "char", "int", "float", "double", "void", "bool", "complex", "imaginary" };
const char *DeclarationNode::modifierName[] = { "signed", "unsigned", "short", "long" };
const char *DeclarationNode::tyConName[] = { "struct", "union", "context" };
const char *DeclarationNode::typeClassName[] = { "type", "dtype", "ftype" };

UniqueName DeclarationNode::anonymous( "__anonymous" );

extern LinkageSpec::Type linkage;		/* defined in cfa.y */

DeclarationNode*
DeclarationNode::clone() const
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = maybeClone( type );
  newnode->name = name;
  newnode->storageClasses = storageClasses;
  newnode->bitfieldWidth = maybeClone( bitfieldWidth );
  newnode->hasEllipsis = hasEllipsis;
  newnode->initializer = initializer;
  newnode->next = maybeClone( next );
  newnode->linkage = linkage;
  return newnode;
}

DeclarationNode::DeclarationNode()
  : type( 0 ), bitfieldWidth( 0 ), initializer( 0 ), hasEllipsis( false ), linkage( ::linkage )
{
}

DeclarationNode::~DeclarationNode()
{
  delete type;
  delete bitfieldWidth;
  delete initializer;
}

bool
DeclarationNode::get_hasEllipsis() const
{
  return hasEllipsis;
}

const char *storageClassName[] =
{
  // order must correspond with DeclarationNode::StorageClass
  "static",
  "auto",
  "extern",
  "register",
  "inline",
  "fortran",
};

void
DeclarationNode::print( std::ostream &os, int indent ) const
{
  os << string(indent,  ' ');
  if( name == "" ) {
///     os << "An unnamed ";
  } else {
    os << name << ": a ";
  }
  if( linkage != LinkageSpec::Cforall ) {
    os << LinkageSpec::toString( linkage ) << " ";
  }
  printEnums( storageClasses.begin(), storageClasses.end(), storageClassName, os );
  if( type ) {
    type->print( os, indent );
  } else {
    os << "untyped entity ";
  }
  if( bitfieldWidth ) {
    os << endl << string(indent+2,  ' ') << "with bitfield width ";
    bitfieldWidth->printOneLine( os );
  }

  if( initializer != 0 ) {
    os << endl << string(indent+2,  ' ') << "with initializer ";
    initializer->printOneLine( os );
  }

  os << endl;
}

void
DeclarationNode::printList( std::ostream &os, int indent ) const
{
  ParseNode::printList( os, indent );
  if( hasEllipsis ) {
    os << string( indent, ' ' )  << "and a variable number of other arguments" << endl;
  }
}

DeclarationNode *
DeclarationNode::newFunction( std::string* name, DeclarationNode *ret, DeclarationNode *param, StatementNode *body, bool newStyle )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->name = assign_strptr( name );

  newnode->type = new TypeData( TypeData::Function );
  newnode->type->function->params = param;
  newnode->type->function->newStyle = newStyle;
  newnode->type->function->body = body;
  if( body ) {
    newnode->type->function->hasBody = true;
  }

  if( ret ) {
    newnode->type->base = ret->type;
    ret->type = 0;
    delete ret;
  }

  return newnode;
}

DeclarationNode *
DeclarationNode::newQualifier( Qualifier q )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = new TypeData();
  newnode->type->qualifiers.push_back( q );
  return newnode;
}

DeclarationNode *
DeclarationNode::newStorageClass( StorageClass sc )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->storageClasses.push_back( sc );
  return newnode;
}

DeclarationNode *
DeclarationNode::newBasicType( BasicType bt )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = new TypeData( TypeData::Basic );
  newnode->type->basic->typeSpec.push_back( bt );
  return newnode;
}

DeclarationNode *
DeclarationNode::newModifier( Modifier mod )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = new TypeData( TypeData::Basic );
  newnode->type->basic->modifiers.push_back( mod );
  return newnode;
}

DeclarationNode *
DeclarationNode::newForall( DeclarationNode* forall )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = new TypeData( TypeData::Unknown );
  newnode->type->forall = forall;
  return newnode;
}

DeclarationNode *
DeclarationNode::newFromTypedef( std::string* name )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = new TypeData( TypeData::SymbolicInst );
  newnode->type->symbolic->name = assign_strptr( name );
  newnode->type->symbolic->isTypedef = true;
  newnode->type->symbolic->params = 0;
  return newnode;
}

DeclarationNode *
DeclarationNode::newAggregate( TyCon kind, std::string* name, DeclarationNode *formals, ExpressionNode *actuals, DeclarationNode *fields )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = new TypeData( TypeData::Aggregate );
  newnode->type->aggregate->kind = kind;
  newnode->type->aggregate->name = assign_strptr( name );
  if( newnode->type->aggregate->name == "" ) {
    newnode->type->aggregate->name = DeclarationNode::anonymous.newName();
  }
  newnode->type->aggregate->params = formals;
  newnode->type->aggregate->actuals = actuals;
  newnode->type->aggregate->members = fields;
  return newnode;
}

DeclarationNode *
DeclarationNode::newEnum( std::string *name, DeclarationNode *constants )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->name = assign_strptr( name );
  newnode->type = new TypeData( TypeData::Enum );
  newnode->type->enumeration->name = newnode->name;
  if( newnode->type->enumeration->name == "" ) {
    newnode->type->enumeration->name = DeclarationNode::anonymous.newName();
  }
  newnode->type->enumeration->constants = constants;
  return newnode;
}

DeclarationNode *
DeclarationNode::newEnumConstant( std::string* name, ExpressionNode *constant )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->name = assign_strptr( name );
  // do something with the constant
  return newnode;
}

DeclarationNode *
DeclarationNode::newName( std::string* name )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->name = assign_strptr( name );
  return newnode;
}

DeclarationNode *
DeclarationNode::newFromTypeGen( std::string* name, ExpressionNode *params )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = new TypeData( TypeData::SymbolicInst );
  newnode->type->symbolic->name = assign_strptr( name );
  newnode->type->symbolic->isTypedef = false;
  newnode->type->symbolic->actuals = params;
  return newnode;
}

DeclarationNode *
DeclarationNode::newTypeParam( TypeClass tc, std::string* name )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->name = assign_strptr( name );
  newnode->type = new TypeData( TypeData::Variable );
  newnode->type->variable->tyClass = tc;
  newnode->type->variable->name = newnode->name;
  return newnode;
}

DeclarationNode *
DeclarationNode::newContext( std::string *name, DeclarationNode *params, DeclarationNode *asserts )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = new TypeData( TypeData::Aggregate );
  newnode->type->aggregate->kind = Context;
  newnode->type->aggregate->params = params;
  newnode->type->aggregate->members = asserts;
  newnode->type->aggregate->name = assign_strptr( name );
  return newnode;
}

DeclarationNode *
DeclarationNode::newContextUse( std::string *name, ExpressionNode *params )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = new TypeData( TypeData::AggregateInst );
  newnode->type->aggInst->aggregate = new TypeData( TypeData::Aggregate );
  newnode->type->aggInst->aggregate->aggregate->kind = Context;
  newnode->type->aggInst->aggregate->aggregate->name = assign_strptr( name );
  newnode->type->aggInst->params = params;
  return newnode;
}

DeclarationNode *
DeclarationNode::newTypeDecl( std::string *name, DeclarationNode *typeParams )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->name = assign_strptr( name );
  newnode->type = new TypeData( TypeData::Symbolic );
  newnode->type->symbolic->isTypedef = false;
  newnode->type->symbolic->params = typeParams;
  newnode->type->symbolic->name = newnode->name;
  return newnode;
}

DeclarationNode *
DeclarationNode::newPointer( DeclarationNode *qualifiers )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = new TypeData( TypeData::Pointer );
  return newnode->addQualifiers( qualifiers );
}

DeclarationNode *
DeclarationNode::newArray( ExpressionNode *size, DeclarationNode *qualifiers, bool isStatic )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = new TypeData( TypeData::Array );
  newnode->type->array->dimension = size;
  newnode->type->array->isStatic = isStatic;
  newnode->type->array->isVarLen = false;
  return newnode->addQualifiers( qualifiers );
}

DeclarationNode *
DeclarationNode::newVarArray( DeclarationNode *qualifiers )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = new TypeData( TypeData::Array );
  newnode->type->array->dimension = 0;
  newnode->type->array->isStatic = false;
  newnode->type->array->isVarLen = true;
  return newnode->addQualifiers( qualifiers );
}

DeclarationNode *
DeclarationNode::newBitfield( ExpressionNode *size )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->bitfieldWidth = size;
  return newnode;
}

DeclarationNode *
DeclarationNode::newTuple( DeclarationNode *members )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = new TypeData( TypeData::Tuple );
  newnode->type->tuple->members = members;
  return newnode;
}

DeclarationNode *
DeclarationNode::newTypeof( ExpressionNode *expr )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = new TypeData( TypeData::Typeof );
  newnode->type->typeexpr->expr = expr;
  return newnode;
}

DeclarationNode *
DeclarationNode::newAttr( std::string *name, ExpressionNode *expr )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = new TypeData( TypeData::Attr );
  newnode->type->attr->name = assign_strptr( name );
  newnode->type->attr->expr = expr;
  return newnode;
}

DeclarationNode *
DeclarationNode::newAttr( std::string *name, DeclarationNode *type )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = new TypeData( TypeData::Attr );
  newnode->type->attr->name = assign_strptr( name );
  newnode->type->attr->type = type;
  return newnode;
}

static void
addQualifiersToType( TypeData *&src, TypeData *dst )
{
  if( src && dst ) {
    if( src->forall && dst->kind == TypeData::Function ) {
      if( dst->forall ) {
	dst->forall->appendList( src->forall );
      } else {
	dst->forall = src->forall;
      }
      src->forall = 0;
    }
    if( dst->base ) {
      addQualifiersToType( src, dst->base );
    } else if( dst->kind == TypeData::Function ) {
      dst->base = src;
      src = 0;
    } else {
      dst->qualifiers.splice( dst->qualifiers.end(), src->qualifiers );
    }
  }
}
      
DeclarationNode *
DeclarationNode::addQualifiers( DeclarationNode *q )
{
  if( q ) {
    storageClasses.splice( storageClasses.end(), q->storageClasses );
    if( q->type ) {
      if( !type ) {
	type = new TypeData;
      }
      addQualifiersToType( q->type, type );
      if( q->type && q->type->forall ) {
        if( type->forall ) {
          type->forall->appendList( q->type->forall );
        } else {
          type->forall = q->type->forall;
        }
        q->type->forall = 0;
      }
    }
  }
  delete q;
  return this;
}

DeclarationNode *
DeclarationNode::copyStorageClasses( DeclarationNode *q )
{
  storageClasses = q->storageClasses;
  return this;
}

static void
addTypeToType( TypeData *&src, TypeData *&dst )
{
  if( src && dst ) {
    if( src->forall && dst->kind == TypeData::Function ) {
      if( dst->forall ) {
        dst->forall->appendList( src->forall );
      } else {
        dst->forall = src->forall;
      }
      src->forall = 0;
    }
    if( dst->base ) {
      addTypeToType( src, dst->base );
    } else {
      switch( dst->kind ) {
      case TypeData::Unknown:
	src->qualifiers.splice( src->qualifiers.end(), dst->qualifiers );
	dst = src;
	src = 0;
	break;

      case TypeData::Basic:
	dst->qualifiers.splice( dst->qualifiers.end(), src->qualifiers );
        if( src->kind != TypeData::Unknown ) {
	  assert( src->kind == TypeData::Basic );
	  dst->basic->modifiers.splice( dst->basic->modifiers.end(), src->basic->modifiers );
	  dst->basic->typeSpec.splice( dst->basic->typeSpec.end(), src->basic->typeSpec );
	}
	break;

      default:
        switch( src->kind ) {
        case TypeData::Aggregate:
        case TypeData::Enum:
          dst->base = new TypeData( TypeData::AggregateInst );
          dst->base->aggInst->aggregate = src;
          if( src->kind == TypeData::Aggregate ) {
            dst->base->aggInst->params = maybeClone( src->aggregate->actuals );
          }
          dst->base->qualifiers.splice( dst->base->qualifiers.end(), src->qualifiers );
          src = 0;
          break;
          
        default:
          if( dst->forall ) {
            dst->forall->appendList( src->forall );
          } else {
            dst->forall = src->forall;
          }
          src->forall = 0;
          dst->base = src;
          src = 0;
        }
      }
    }
  }
}

DeclarationNode *
DeclarationNode::addType( DeclarationNode *o )
{
  if( o ) {
    storageClasses.splice( storageClasses.end(), o->storageClasses );
    if ( o->type ) {
      if( !type ) {
        if( o->type->kind == TypeData::Aggregate || o->type->kind == TypeData::Enum ) {
	  type = new TypeData( TypeData::AggregateInst );
	  type->aggInst->aggregate = o->type;
	  if( o->type->kind == TypeData::Aggregate ) {
	    type->aggInst->params = maybeClone( o->type->aggregate->actuals );
          }
          type->qualifiers.splice( type->qualifiers.end(), o->type->qualifiers );
	} else {
	  type = o->type;
	}
	o->type = 0;
      } else {
	addTypeToType( o->type, type );
      }
    }
    if( o->bitfieldWidth ) {
      bitfieldWidth = o->bitfieldWidth;
    }
  }
  delete o;
  return this;
}

DeclarationNode *
DeclarationNode::addTypedef()
{
  TypeData *newtype = new TypeData( TypeData::Symbolic );
  newtype->symbolic->params = 0;
  newtype->symbolic->isTypedef = true;
  newtype->symbolic->name = name;
  newtype->base = type;
  type = newtype;
  return this;
}

DeclarationNode *
DeclarationNode::addAssertions( DeclarationNode* assertions )
{
  assert( type );
  switch( type->kind ) {
  case TypeData::Symbolic:
    if( type->symbolic->assertions ) {
      type->symbolic->assertions->appendList( assertions );
    } else {
      type->symbolic->assertions = assertions;
    }
    break;
    
  case TypeData::Variable:
    if( type->variable->assertions ) {
      type->variable->assertions->appendList( assertions );
    } else {
      type->variable->assertions = assertions;
    }
    break;
    
  default:
    assert( false );
  }
    
  return this;
}

DeclarationNode *
DeclarationNode::addName( std::string* newname )
{
  name = assign_strptr( newname );
  return this;
}

DeclarationNode *
DeclarationNode::addBitfield( ExpressionNode *size )
{
  bitfieldWidth = size;
  return this;
}

DeclarationNode *
DeclarationNode::addVarArgs()
{
  assert( type );
  hasEllipsis = true;
  return this;
}

DeclarationNode *
DeclarationNode::addFunctionBody( StatementNode *body )
{
  assert( type );
  assert( type->kind == TypeData::Function );
  assert( type->function->body == 0 );
  type->function->body = body;
  type->function->hasBody = true;
  return this;
}

DeclarationNode *
DeclarationNode::addOldDeclList( DeclarationNode *list )
{
  assert( type );
  assert( type->kind == TypeData::Function );
  assert( type->function->oldDeclList == 0 );
  type->function->oldDeclList = list;
  return this;
}

static void
setBase( TypeData *&type, TypeData *newType )
{
  if( type ) {
    TypeData *prevBase = type;
    TypeData *curBase = type->base;
    while( curBase != 0 ) {
      prevBase = curBase;
      curBase = curBase->base;
    }
    prevBase->base = newType;
  } else {
    type = newType;
  }
}

DeclarationNode *
DeclarationNode::addPointer( DeclarationNode *p )
{
  if( p ) {
    assert( p->type->kind == TypeData::Pointer );
    setBase( type, p->type );
    p->type = 0;
    delete p;
  }
  return this;
}

DeclarationNode *
DeclarationNode::addArray( DeclarationNode *a )
{
  if( a ) {
    assert( a->type->kind == TypeData::Array );
    setBase( type, a->type );
    a->type = 0;
    delete a;
  }
  return this;
}

DeclarationNode *
DeclarationNode::addNewPointer( DeclarationNode *p )
{
  if( p ) {
    assert( p->type->kind == TypeData::Pointer );
    if( type ) {
      switch( type->kind ) {
      case TypeData::Aggregate:
      case TypeData::Enum:
        p->type->base = new TypeData( TypeData::AggregateInst );
        p->type->base->aggInst->aggregate = type;
       	if( type->kind == TypeData::Aggregate ) {
       	  p->type->base->aggInst->params = maybeClone( type->aggregate->actuals );
       	}
        p->type->base->qualifiers.splice( p->type->base->qualifiers.end(), type->qualifiers );
        break;
        
      default:
        p->type->base = type;
      }
      type = 0;
    }
    delete this;
    return p;
  } else {
    return this;
  }
}

static TypeData *
findLast( TypeData *a )
{
  assert( a );
  TypeData *cur = a;
  while( cur->base ) {
    cur = cur->base;
  }
  return cur;
}

DeclarationNode *
DeclarationNode::addNewArray( DeclarationNode *a )
{
  if( a ) {
    assert( a->type->kind == TypeData::Array );
    TypeData *lastArray = findLast( a->type );
    if( type ) {  
      switch( type->kind ) {
      case TypeData::Aggregate:
      case TypeData::Enum:
        lastArray->base = new TypeData( TypeData::AggregateInst );
        lastArray->base->aggInst->aggregate = type;
        if( type->kind == TypeData::Aggregate ) {
          lastArray->base->aggInst->params = maybeClone( type->aggregate->actuals );
        }
        lastArray->base->qualifiers.splice( lastArray->base->qualifiers.end(), type->qualifiers );
        break;
        
      default:
        lastArray->base = type;
      }
      type = 0;
    }
    delete this;
    return a;
  } else {
    return this;
  }
}

DeclarationNode *
DeclarationNode::addParamList( DeclarationNode *params )
{
  TypeData *ftype = new TypeData( TypeData::Function );
  ftype->function->params = params;
  setBase( type, ftype );
  return this;
}

static TypeData*
addIdListToType( TypeData *type, DeclarationNode *ids )
{
  if( type ) {
    if( type->kind != TypeData::Function ) {
      type->base = addIdListToType( type->base, ids );
    } else {
      type->function->idList = ids;
    }
    return type;
  } else {
    TypeData *newtype = new TypeData( TypeData::Function );
    newtype->function->idList = ids;
    return newtype;
  }
}
    
DeclarationNode *
DeclarationNode::addIdList( DeclarationNode *ids )
{
  type = addIdListToType( type, ids );
  return this;
}

DeclarationNode *
DeclarationNode::addInitializer( InitializerNode *init )
{
  //assert
  initializer = init;
  return this;
}

DeclarationNode *
DeclarationNode::cloneBaseType( string *newName )
{
  DeclarationNode *newnode = new DeclarationNode;
  TypeData *srcType = type;
  while( srcType->base ) {
    srcType = srcType->base;
  }
  newnode->type = maybeClone( srcType );
  if( newnode->type->kind == TypeData::AggregateInst ) {
    // don't duplicate members
    if( newnode->type->aggInst->aggregate->kind == TypeData::Enum ) {
      delete newnode->type->aggInst->aggregate->enumeration->constants;
      newnode->type->aggInst->aggregate->enumeration->constants = 0;
    } else {
      assert( newnode->type->aggInst->aggregate->kind == TypeData::Aggregate );
      delete newnode->type->aggInst->aggregate->aggregate->members;
      newnode->type->aggInst->aggregate->aggregate->members = 0;
    }
  }
  newnode->type->forall = maybeClone( type->forall );
  newnode->storageClasses = storageClasses;
  newnode->name = assign_strptr( newName );
  return newnode;
}

DeclarationNode *
DeclarationNode::cloneBaseType( DeclarationNode *o )
{
  if( o ) {
    o->storageClasses.insert( o->storageClasses.end(), storageClasses.begin(), storageClasses.end() );
    if ( type ) {
      TypeData *srcType = type;
      while( srcType->base ) {
	srcType = srcType->base;
      }
      TypeData *newType = srcType->clone();
      if( newType->kind == TypeData::AggregateInst ) {
        // don't duplicate members
        if( newType->aggInst->aggregate->kind == TypeData::Enum ) {
          delete newType->aggInst->aggregate->enumeration->constants;
          newType->aggInst->aggregate->enumeration->constants = 0;
        } else {
          assert( newType->aggInst->aggregate->kind == TypeData::Aggregate );
          delete newType->aggInst->aggregate->aggregate->members;
          newType->aggInst->aggregate->aggregate->members = 0;
        }
      }
      newType->forall = maybeClone( type->forall );
      if( !o->type ) {
	o->type = newType;
      } else {
	addTypeToType( newType, o->type );
	delete newType;
      }
    }
  }
  return o;
}

DeclarationNode *
DeclarationNode::cloneType( string *newName )
{
  DeclarationNode *newnode = new DeclarationNode;
  newnode->type = maybeClone( type );
  newnode->storageClasses = storageClasses;
  newnode->name = assign_strptr( newName );
  return newnode;
}

DeclarationNode *
DeclarationNode::cloneType( DeclarationNode *o )
{
  if( o ) {
    o->storageClasses.insert( o->storageClasses.end(), storageClasses.begin(), storageClasses.end() );
    if ( type ) {
      TypeData *newType = type->clone();
      if( !o->type ) {
	o->type = newType;
      } else {
	addTypeToType( newType, o->type );
	delete newType;
      }
    }
  }
  return o;
}

DeclarationNode *
DeclarationNode::appendList( DeclarationNode *node )
{
  if( node != 0 ) {
    set_link( node );
  }
  return this;
}

DeclarationNode*
DeclarationNode::extractAggregate() const
{
  if( type ) {
    TypeData *ret = type->extractAggregate();
    if( ret ) {
      DeclarationNode *newnode = new DeclarationNode;
      newnode->type = ret;
      return newnode;
    } else {
      return 0;
    }
  } else {
    return 0;
  }
}

void buildList( const DeclarationNode *firstNode, std::list< Declaration* > &outputList )
{
  SemanticError errors;
  std::back_insert_iterator< std::list< Declaration* > > out( outputList );
  const DeclarationNode *cur = firstNode;
  while( cur ) {
    try {
      if( DeclarationNode *extr = cur->extractAggregate() ) {
	// handle the case where a structure declaration is contained within an object or type
	// declaration
	Declaration *decl = extr->build();
	if( decl ) {
	  *out++ = decl;
	}
      }
      Declaration *decl = cur->build();
      if( decl ) {
        *out++ = decl;
      }
    } catch( SemanticError &e ) {
      errors.append( e );
    }
    cur = dynamic_cast< DeclarationNode* >( cur->get_link() );
  }
  if( !errors.isEmpty() ) {
    throw errors;
  }
}

void buildList( const DeclarationNode *firstNode, std::list< DeclarationWithType* > &outputList )
{
  SemanticError errors;
  std::back_insert_iterator< std::list< DeclarationWithType* > > out( outputList );
  const DeclarationNode *cur = firstNode;
  while( cur ) {
    try {
///       if( DeclarationNode *extr = cur->extractAggregate() ) {
/// 	// handle the case where a structure declaration is contained within an object or type
/// 	// declaration
/// 	Declaration *decl = extr->build();
/// 	if( decl ) {
///           *out++ = decl;
/// 	}
///       }
      Declaration *decl = cur->build();
      if( decl ) {
        if( DeclarationWithType *dwt = dynamic_cast< DeclarationWithType* >( decl ) ) {
          *out++ = dwt;
        } else if( StructDecl *agg = dynamic_cast< StructDecl* >( decl ) ) {
          StructInstType *inst = new StructInstType( Type::Qualifiers(), agg->get_name() );
          *out++ = new ObjectDecl( "", Declaration::NoStorageClass, linkage, 0, inst, 0 );
          delete agg;
        } else if( UnionDecl *agg = dynamic_cast< UnionDecl* >( decl ) ) {
          UnionInstType *inst = new UnionInstType( Type::Qualifiers(), agg->get_name() );
          *out++ = new ObjectDecl( "", Declaration::NoStorageClass, linkage, 0, inst, 0 );
        }
      }
    } catch( SemanticError &e ) {
      errors.append( e );
    }
    cur = dynamic_cast< DeclarationNode* >( cur->get_link() );
  }
  if( !errors.isEmpty() ) {
    throw errors;
  }
}

void buildTypeList( const DeclarationNode *firstNode, std::list< Type* > &outputList )
{
  SemanticError errors;
  std::back_insert_iterator< std::list< Type* > > out( outputList );
  const DeclarationNode *cur = firstNode;
  while( cur ) {
    try {
      *out++ = cur->buildType();
    } catch( SemanticError &e ) {
      errors.append( e );
    }
    cur = dynamic_cast< DeclarationNode* >( cur->get_link() );
  }
  if( !errors.isEmpty() ) {
    throw errors;
  }
}

Declaration *
DeclarationNode::build() const
{

  if( !type ) {
    if( buildInline() ) {
      throw SemanticError( "invalid inline specification in declaration of ", this );
    } else {
      return new ObjectDecl( name, buildStorageClass(), linkage, maybeBuild< Expression >( bitfieldWidth ), 0, maybeBuild< Initializer >( initializer ) );
    }
  } else {
    Declaration *newDecl = type->buildDecl( name, buildStorageClass(), maybeBuild< Expression >( bitfieldWidth ), buildInline(), linkage, maybeBuild< Initializer >(initializer) );
    return newDecl;
  }
  // we should never get here
  assert( false );
  return 0;
}

Type *
DeclarationNode::buildType() const
{

  assert( type );
  
  switch( type->kind ) {
  case TypeData::Enum:
    return new EnumInstType( type->buildQualifiers(), type->enumeration->name );
    
  case TypeData::Aggregate: {
    ReferenceToType *ret;
    switch( type->aggregate->kind ) {
    case DeclarationNode::Struct:
      ret = new StructInstType( type->buildQualifiers(), type->aggregate->name );
      break;

    case DeclarationNode::Union:
      ret = new UnionInstType( type->buildQualifiers(), type->aggregate->name );
      break;

    case DeclarationNode::Context:
      ret = new ContextInstType( type->buildQualifiers(), type->aggregate->name );
      break;

    default:
      assert( false );
    }
    buildList( type->aggregate->actuals, ret->get_parameters() );
    return ret;
  }
  
  case TypeData::Symbolic: {
    TypeInstType *ret = new TypeInstType( type->buildQualifiers(), type->symbolic->name, false );
    buildList( type->symbolic->actuals, ret->get_parameters() );
    return ret;
  }
  
  default:
    return type->build();
  }
}

Declaration::StorageClass 
DeclarationNode::buildStorageClass() const
{
  static const Declaration::StorageClass scMap[] = {  
    Declaration::Static,
    Declaration::Auto,
    Declaration::Extern,
    Declaration::Register,
    Declaration::NoStorageClass, // inline
    Declaration::Fortran
  };  
  
  Declaration::StorageClass ret = Declaration::NoStorageClass;
  for( std::list< StorageClass >::const_iterator i = storageClasses.begin(); i != storageClasses.end(); ++i ) {
    assert( unsigned( *i ) < sizeof( scMap ) / sizeof( scMap[0] ) );
    if( *i == Inline ) continue;
    if( ret == Declaration::NoStorageClass ) {
      ret = scMap[ *i ];
    } else {
      throw SemanticError( "invalid combination of storage classes in declaration of ", this );
    }
  }
  return ret;
}

bool 
DeclarationNode::buildInline() const
{
  std::list< StorageClass >::const_iterator first = std::find( storageClasses.begin(), storageClasses.end(), Inline );
  if( first == storageClasses.end() ) {
    return false;
  } else {
    std::list< StorageClass >::const_iterator next = std::find( ++first, storageClasses.end(), Inline );
    if( next == storageClasses.end() ) {
      return true;
    } else {
      throw SemanticError( "duplicate inline specification in declaration of ", this );
    }
  }
  // we should never get here
  return false;
}
