/* main_parser.cc */

#include "parser.h"
#include <ctype.h>
#include "id_lookup.h"
#include <fstream.h>
#include "root.h"
#include "preparser.h"
#include "pattern_match.h"
#include "cor_syn_iface.h"
#include <sys/ddi.h>

static slist<dc_arg *> la_stack;
inline void la_stack_pop( int n ) { 
  n = min( n, la_stack.size() );
  for( int i = 0 ; i < n ; i++ ) la_stack.pop(); }
inline void la_stack_push( dc_arg &a ) {
  la_stack.push( &a ); }
inline dc_arg *la_stack_match( cstring lbl ) {
  dc_arg *a;
  forall( a, la_stack ) { if( a->match( lbl ) ) return a; }
  return nil;
}

/* all return true on error */
bool parse_f_list( token_queue &, dc_op &, dc_label *, dc_label * );
bool pass_cstatus( token_queue & );

/* nil on error */
dc_func *parse_func( token_queue &, dc_label *, dc_label * );
dc_func *parse_fcall( token_queue &, dc_label *, dc_label * );
dc_func *parse_iter_loop( token_queue &, dc_label *, dc_label * );

bool parse_global_def( token_queue &, dc_component * );
bool pass_enum_def( token_queue & );
bool pass_symbol_def( token_queue & );

bool is_fn_of_type( const parseTag pT, const fntype type ) {
  if( ( ( int )pT <= FN_START ) || ( ( int )pT >= FN_END ) ) return false;
  
  fntype T = fn_list[pT - FN_START - 1].type;
  if( T == type ) return true;
  switch( type ) {
  case UNARY_OP : case OP :
    return T == U_OP;
  default : return false;
  }
}

bool get_fn_of_type( const parseTag pT, int &index, const fntype type ) {
  if( is_fn_of_type( pT, type ) ) {
    index = pT - FN_START - 1;
    return false;
  } return true;
}

dc_label *restricted_lookup( string label, dc_node *parent ) {
  if( parent ) {
    return parent->lookup_child( label, true );
  } else {
    dic_item child_it = root.label_list.lookup( label );
    if( child_it == nil ) return nil;
    return root.label_list.inf( child_it );
  }
}

int parse( string filename, dc_component *parent = nil ) {
  token_queue Q( filename );
  return parse( Q, parent );
}

int parse( istream &stream, dc_component *parent = nil ) {
  token_queue Q( stream );
  return parse( Q, parent );
}

int parse( token_queue &Q, dc_component *parent = nil ) {
  slist<dc_label *> objs_defined;
  if( preparse( Q, parent, objs_defined ) ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "parse - error preparsing\n";
    }
    Q.clear();
    return 0;
  }

  cerr << "PREPARSING FINISHED\n";

  Q.rewind();
  token *t;
  int count = 0;

  while( ( t = Q.peek() ) != nil ) {
    if( parse_block( Q, parent ) ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "parse -- failed to parse block starting at " 
	     << t->pos_string() << "\n";
      }
      dc_label *id;
      forall( id, objs_defined ) delete( id );

      dc_modification *mod;
      forall( mod, modify_list ) {
	delete( mod );
      }
      modify_list.clear();
      objs_defined.clear();
      Q.clear();
      return 0;
    }
    count++;
  }
  objs_defined.clear();
  Q.clear();
  modify_list.clear();

  return count;
}

bool parse_component( token_queue &Q, dc_component *parent ) {
  token *t = Q.peek();
  if( !t ) return true;
  string pos = t->pos_string();

  /* get component tag */
  if( check_tag( Q, Component_Tag ) ) {
    return true;
  }
  
  /* get label */
  t = Q.pop();
  if( !valid_label( t ) ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "parse_component -- expected valid label near " << pos 
	   << ".  found " << *t << "\n";
    }
    return true;
  }
  string label = ( ( string_token * )t )->get();

  /* check for [':' <template_label>] */
  if( !check_tag( Q, Colon_Tag ) ) {
    string template_label;
    if( expect_path( Q, template_label, "parse_component" ) ) {
      return nil;
    }
  }

  /* get { */  
  if( expect_tag( Q, L_Bracket_Tag, "parse_component" ) ) {
    return true;
  }

  dc_component *c = ( dc_component * )restricted_lookup( label, parent );

  if( !c ) { 
    dc_trace( TRACE_ERROR ) {
      cerr << "parse_component -- \"" << label << "\"not found at " 
	   << ( parent ? parent->full_type() : string( "root" ) ) << "\n";
    }
    return true;
  }
  if( c->sub_type() != Component_t ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "parse_component -- expected " << c->full_type() 
	   << " to be a component\n";
    }
    return true;
  }
  
  /* get type */
  if( parse_utypes( Q, *c ) ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "parse_component -- error parsing user-defined types for " 
	   << c->full_type() << "\n";
    }
    return true;
  }

  /* get children */
  while( ( t = Q.peek() ) != nil && *t != R_Bracket_Tag ) {
    if( *t == Frozen_Tag || *t == Dormant_Tag ) {
      if( pass_cstatus( Q ) ) {
	delete( c );
	return nil;
      }
    } else if( parse_block( Q, c ) ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "parse_component -- error parsing contents of component \""
	     << label << "\" starting at " << pos << ". block starts at " 
	     << t->pos_string() << "\n";
      }
      return true;
    }
  }
  
  /* test for end */
  if( expect_tag( Q, R_Bracket_Tag, "parse_component" ) ) {
    return true;
  }

  return false;
}

bool pass_cstatus( token_queue &Q ) {
  if( !check_tag( Q, Dormant_Tag ) || !check_tag( Q, Frozen_Tag ) ) {
    if( expect_tag( Q, Equal_Tag, "pass_cstatus" ) ) {
      return true;
    }
    bool b;
    if( parse_bool( Q, b ) ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "pass_cstatus -- expected status boolean near "
	     << Q.pos_string() << "\n";
      }
      return true;
    }
    return expect_tag( Q, Semicolon_Tag, "pass_cstatus" );
  }
  return false;
}

bool parse_element( token_queue &Q, dc_component *parent ) {
  string pos = Q.pos_string();

  check_tag( Q, Transparent_Tag );

  token *t = Q.peek();
  if( !t ) {
    return true;
  }

  /* optional data type */
  if( t->is_tag() ) {
    if( get_dtype( ( ( tag_token * )t )->get() ) != Undef_t ) {
      Q.pop();
    }
  } else {
    dc_svec_tag e_tag = sb.lookup_svec( ( ( string_token * )t )->get() );
    /* problem here since preparser will only define as Symbol_t if it finds
       an enum label defined before it reached this point, but this may be label
       that conflicts with subsequently defined enum not known by preparser when
       this parsed. */
    if( e_tag != undef_svec ) {
      Q.pop();
    }
  }
  
  bool is_constraint = false;
 
  if( !check_tag( Q, Constraint_Tag ) ) {
    is_constraint = true;
  } else check_tag( Q, Element_Tag );
  
  string label;
  if( expect_label( Q, label, "parse_element" ) ) {
    return true;
  }
  
  dc_element *e;
  e = ( dc_element * )restricted_lookup( label, parent );
    
  if( !e ) {
    dc_trace( TRACE_ERROR ) {
      if( is_constraint ) {
	cerr << "parse_element -- \"" << label << "\" from " << pos 
	     << " not found in constraint_list\n";
      } else {
	cerr << "parse_element -- \"" << label << "\" from " << pos 
	     << " not found at " 
	     << ( parent ? parent->full_type() : string( "root" ) ) << "\n";
      }
    }
    return true;
  }

  if( e->sub_type() != Element_t ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "parse_element -- expected " << e->full_type() << " at " << pos
	   << " to be an element\n";
      return true;
    }
  }
  /*dc_type el_t = e->get_rtype();*/

  int on_stack = 0;
  /* check if paren block for args */
  if( !check_tag( Q, L_Paren_Tag ) && check_tag( Q, R_Paren_Tag ) ) {
    int n = 0;
    while( 1 ) {
      t = Q.peek();
      if( t->is_tag() ) {
	if( get_dtype( ( ( tag_token * )t )->get() ) != Undef_t ) {
	  Q.pop();
	}
      } 
      
      if( !valid_label( t = Q.pop() ) ) {
	delete( e );
	dc_trace( TRACE_ERROR ) {
	  cerr << "parse_element -- error parsing label for arg " << n
	       << " near " << t->pos_string() << "\n";
	}
	return true;
      }
      string name = ( ( string_token * )t )->get();

      dc_arg *a = e->match( name );
      if( !a ) {
	dc_trace( TRACE_ERROR ) {
	  cerr << "parse_element -- error matching arg " << n << "\"" << name
	       << "\" of " << e->full_type() 
	       << " near " << Q.pos_string() << "\n";
	}
	la_stack_pop( on_stack );
	return true;
      }
      la_stack_push( *a );
      on_stack++;

      dc_type arg_t = a->get_rtype();
      dc_func *default_val = nil;
      if( !check_tag( Q, Equal_Tag ) ) {
	default_val = parse_function( Q, e, is_constraint ? ( dc_label * )parent
				      : ( dc_label * )e/*, arg_t*/ );
	if( !default_val ) {
	  dc_trace( TRACE_ERROR ) {
	    cerr << "parse_element -- error parsing default value for argument "
		 << n << " of " << e->full_type() << " near " << Q.pos_string() 
		 << "\n";
	    la_stack_pop( on_stack );
	    return true;
	  }
	}
      } else {
	dc_default *deflt = get_default( name, parent );
	dc_data *val;
	if( ( deflt != nil ) && ( ( val = deflt->get() ) != nil ) ) {
	  if( val->sub_type() == arg_t ) {
	    dc_data *copy = ( dc_data *)allocate( val->sub_type() );
	    set_data( *copy, *val );
	    default_val = new dc_const( *copy );
	  } else {
	    val = cast( *val, arg_t );
	    if( val ) default_val = new dc_const( *val );
	    else {
	      dc_trace( TRACE_WARNING ) {
		cerr << "parse_element -- default value for " << name 
		     << " cannot be cast to arg " << n << " \"" << name 
		     << "\" of " << e->full_type() << "\n";
	      }
	    }
	  }
	}
      }
      
      if( default_val ) {
	a->set_default( default_val );
      }
      if( check_tag( Q, Comma_Tag ) ) {
	break;
      }
    
      n++;
    }

    if( expect_tag( Q, R_Paren_Tag, "parse_element" ) ) {
      la_stack_pop( on_stack );
      return true;
    }
  }
  
  /* check '=' */
  if( expect_tag( Q, Equal_Tag, "parse_element" ) ) {
    la_stack_pop( on_stack );
    return true;
  }

  /* get function */
  dc_func *f = nil;
  if( check_tag( Q, Nil_Tag ) ) {
    f = parse_function( Q, e, is_constraint ? ( dc_label * )parent
			: ( dc_label * )e/*, el_t*/ );
    if( f == nil ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "parse_element -- error parsing eval function for element \""
	     << label << "\" starting at " << pos << "\n";
      }
      la_stack_pop( on_stack );
      return true;
    }
  }

    /* get optional initial value */
  if( !check_tag( Q, Colon_Tag ) ) {
    dc_data *initial = parse_data( Q, e, e/*, el_t*/ );
    if( !initial ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "parse_element -- error parsing initial value for element \""
	     << label << "\" starting at " << pos << "\n";
      }
      la_stack_pop( on_stack );
      return true;    
    }
    if( e->init( *initial ) ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "parse_element -- error setting initial value for element \""
	     << label << "\" starting at " << pos << "\n";
      }
      la_stack_pop( on_stack );
      return true;
    }
  } else { /* check defaults for initial value */
    dc_default *deflt = get_default( label, parent );
    dc_data *initial;
    if( ( deflt != nil ) && ( ( initial = deflt->get() ) != nil ) ) {
      dc_data *copy = ( dc_data * )allocate( initial->sub_type() );
      set_data( *copy, *initial );
      e->init( *copy, true );
    }
  }

  /* init has to precede set_func since set_func with a simplifiable function 
     will override initial */
  if( f != nil && e->set_func( *f ) ) { 
    dc_trace( TRACE_ERROR ) {
      cerr << "parse_element -- failed to set eval function " << " to ( " << *f
	   << " ) for " << e->full_type() <<  " at " << pos << "\n";
    }
    la_stack_pop( on_stack );
    return true;
  }

  if( !check_tag( Q, Comma_Tag ) ) {
    if( expect_tag( Q, Clock_Tag, "parse_element" ) ) {
      la_stack_pop( on_stack );
      return true;
    }
    string clock_path;
    if( expect_path( Q, clock_path, "parse_element" ) ) {
      la_stack_pop( on_stack );
      return true;
    }
    /* check that clock_path points to a valid clock so that error can be 
       flagged with line number rather than coming up as a rehashing error */
    dc_clock *clk = ( dc_clock * )t_search( clock_path, Clock_t, parent );
    if( clk == nil ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "parse_element -- no clock matching \"" << clock_path 
	     << "\" for " << label << " at " << pos << "\n";
      }
      la_stack_pop( on_stack );
      return true;
    }
    
    if( clk != root.iter_counter ) {
      root.iter_counter->add_secondary( *e );
    }

    /* add secondary clocks */
    while( !check_tag( Q, Comma_Tag ) ) {
      if( expect_path( Q, clock_path, "parse_element" ) ) {
	la_stack_pop( on_stack );
	return true;
      }
      dc_clock *clk = ( dc_clock * )t_search( clock_path, Clock_t, parent );
      if( clk == nil ) {
	dc_trace( TRACE_ERROR ) {
	  cerr << "parse_element -- no clock matching \"" << clock_path 
	       << "\" for " << label << " at " << pos << "\n";
	}
	la_stack_pop( on_stack );
	return true;
      }
      clk->add_secondary( *e );
    }
  }
  
  /* check ';' */
  if( expect_tag( Q, Semicolon_Tag, "parse_element" ) ) {
    la_stack_pop( on_stack );
    return true;
  }

  la_stack_pop( on_stack );
  return false;
}

bool parse_clock( token_queue &Q, dc_component *parent ) {
  if( expect_tag( Q, Clock_Tag, "parse_clock" ) ) return true;

  string label;
  if( expect_label( Q, label, "parse_clock" ) ) return true;
  dc_clock *clk = ( dc_clock * )restricted_lookup( label, parent );
  if( clk == nil || clk->type() != Clock_t ) return true;
  return false;
}

bool parse_modify( token_queue &Q, dc_node *parent ) {
  if( check_tag( Q, Modify_Tag ) ) return true;

  check_tag( Q, Component_Tag );

  string path;
  if( expect_path( Q, path, "parse_modify" ) ) return true;

  if( !check_tag( Q, Colon_Tag ) ) {
    if( expect_path( Q, path, "parse_modify" ) ) return true;
  }

  token *t = Q.peek();
  if( !t || t->is_tag() ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "parse_modify -- modification label not found at " 
	   << t->pos_string() << "\n";
    }
    return true;
  }

  string modify_label = ( ( string_token * ) t )->get();

  dc_modification *mod = ( dc_modification* )
    restricted_lookup( modify_label, parent );

  if( mod == nil ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "parse_modify -- failed to locate modification \"" 
	   << modify_label << " at " << t->pos_string() << "\n";
    }
    return true;
  }

  Q.pop();

  if( expect_tag( Q, L_Bracket_Tag, "parse_modify" ) ) return true;

  while( check_tag( Q, R_Bracket_Tag ) ) {
    t = Q.peek();

    if( t == nil ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "parse_modify -- unexpected end of input\n";
      }
      return true;
    }
    bool test;
    if( t->is_tag() ) {
      switch( ( ( tag_token * ) t )->get() ) {
      case Component_Tag : 
	test = parse_component( Q, mod );
	break;
      case Clock_Tag :
	test = parse_clock( Q, mod );
	break;
      case Modify_Tag :
	test = parse_modify( Q, mod );
	break;
      default :
	test = parse_element( Q, mod );
      }
    } else {
      test = parse_element( Q, mod );
    }
    if( test ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "parse_modify -- failed to parse modification block\n";
      }
      return true;
    }
  }

  return false;
}

bool parse_block( token_queue &Q, dc_component *parent ) {
  token *t = Q.peek();
  if( t == nil ) return true;
  if( t->is_tag() ) {
    switch( ( ( tag_token * )t )->get() ) {
    case Component_Tag :
      return parse_component( Q, parent );
      break;
    case Clock_Tag :
      return parse_clock( Q, parent );
      break;
    case Modify_Tag :
      return parse_modify( Q, parent );
      break;
    case GA_Tag :
      return parse_ga( Q, parent );
      break;
    default :
      return parse_global_def( Q, parent ) && parse_element( Q, parent );
    }
  }
  return parse_element( Q, parent );
}

bool parse_arg( token_queue &Q, dc_func *&arg, dc_label *owner, 
		dc_label *search_origin/*, dc_type rtype = Data_t*/ ) {
  bool old = false;
  token *t = Q.peek();
  if( t == nil ) return false;

  dc_type T;
  if( t->is_tag() ) {
    parseTag pT = ( ( tag_token * )t )->get();

    if( get_dtype( pT ) != Undef_t ) {
      dc_data *d = parse_data( Q, owner, search_origin/*, rtype*/ );
      if( d == nil ) return nil;
      arg = new dc_const( *d );
      return false;
    }

    dc_data *d;
    dc_real *r;
    switch( pT ) {
    case Ampersand_Tag :
      d = parse_data( Q, owner, search_origin/*, rtype*/ );
      if( d == nil ) {
	cerr << "parse_arg -- \n";
	return nil;
      }
      arg = new dc_const( *d );
      return false;
      break;
    case Self_Tag :
      Q.pop();
      if( owner && owner->type() != Element_t ) {
	dc_trace( TRACE_ERROR ) {
	  cerr << "parse_arg -- self_ref used outside of element definition\n";
	}
	return true;
      }
      arg = new dc_elvalue( *( ( dc_element * )owner ), true );
      return false;
      break;
    case L_Paren_Tag :
      Q.pop();
      t = Q.peek();
      if( t->is_tag() && ( T = get_dtype( ( ( tag_token * )t )->get() ) )
	  != Undef_t ) {
	token *dtag_tkn = Q.extract();
	t = Q.peek();
	if( t && *t == R_Paren_Tag ) { /* is type cast of form '(' dtag ')' */
	  Q.push_used( dtag_tkn );
	  Q.pop(); /* pop R_Paren_Tag */
	  if( parse_arg( Q, arg, owner, search_origin/*, T*/ ) ) return true;
	  return false;
	} else {
	  Q.push( dtag_tkn );
	}
      }

      /* eat paren and recurse */
      arg = parse_function( Q, owner, search_origin/*, rtype*/ );
      if( expect_tag( Q, R_Paren_Tag, "parse_arg" ) ) {
	delete( arg );
	arg = nil;
	return true;
      }
      return ( arg == nil );
      break;
    case Old_Tag :
      Q.pop();
      t = Q.peek();
      old = true;
      break;
    case Time_Tag :
      if( owner ) {
	Q.pop();
	arg = new dc_tquery( owner, true );
	return false;
      } else {
	dc_trace( TRACE_ERROR ) {
	  cerr << "parse_arg -- error parsing tquery near " << t->pos_string() 
	       << "\n";
	}
	return true;
      }
      break;
    case Delta_Time_Tag :
      if( owner ) {
	Q.pop();
	arg = new dc_tquery( owner, false );
	return false;
      } else {
	dc_trace( TRACE_ERROR ) {
	  cerr << "parse_arg -- error parsing tquery near " << t->pos_string() 
	       << "\n";
	}
	return true;
      }
      break;
    case For_Tag :
      arg = parse_iter_loop( Q, owner, search_origin );
      if( arg == nil ) {
	dc_trace( TRACE_ERROR ) {
	  cerr << "parse_arg -- error parsing iteration loop near "
	       << t->pos_string() << "\n";
	}
	return true;
      }
      return false;
      break;
    case L_SqBracket_Tag :
      /* check for unit cast */
      Q.pop();
      if( expect_tag( Q, Cast_Tag, "parse_arg -- unit cast" ) ) {
	arg = nil;
	return true;
      }

      r = new dc_real( 1 );
      if( parse_uv( Q, r, false ) == uv_pass ) {
	if( expect_tag( Q, R_SqBracket_Tag, "parse_arg -- unit_cast" ) ) {
	  arg = nil;
	  delete( r );
	  return true;
	}
	if( parse_arg( Q, arg, owner, search_origin ) ) {
	  dc_trace( TRACE_ERROR ) {
	    cerr << "parse_arg -- unit cast -- error parsing target near "
		 << Q.pos_string() << "\n";
	  }
	  return true;
	}

	arg = new dc_ucast( *arg, r->get_units(), r->get() );
	delete( r );
	return false;
      }
      cerr << "parse_arg -- unit cast -- expected unit string near " 
	   << Q.pos_string() << "\n";
      arg = nil;
      return true;
      break;
    default :
      /* check unary op */
      int index;
      if( !get_fn_of_type( pT, index, UNARY_OP ) ) {
	Q.pop();
	dc_func *arg2;
	if( parse_arg( Q, arg2, owner, search_origin/*, rtype*/ ) ) {
	  return true;
	}
	arg = new dc_op;
	( ( dc_op * )arg )->set_info( index );
	( ( dc_op * )arg )->add_arg( *arg2 );
	return false;
      }
      /* parse func */
      arg = parse_func( Q, owner, search_origin );
      return ( arg == nil );
    }
  }

  /* parse pathname */
  dc_data *d;
  if( ( d = parse_data( Q, owner, search_origin/*, rtype*/ ) ) != nil ) {
    arg = new dc_const( *d );
    return false;
  } else {
    if( !valid_path( t ) ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "parse_arg -- invalid label near " << t->pos_string() << "\n";
      }
      return true;
    }
    string label = ( ( string_token * )t )->get();
    /* see if function argument matching */
    arg = ( dc_func * )la_stack_match( label );
    if( arg != nil ) {
      Q.pop();
      return false;
    }
    
    token *lbl_token = Q.extract(); /* pop label */
    t = Q.peek();
    if( t && *t == L_Paren_Tag ) {
      Q.push( lbl_token );
      arg = parse_fcall( Q, owner, search_origin );
      return( arg == nil );
    } else {
      Q.push_used( lbl_token );
      arg = new dc_elvalue( label, search_origin );
      if( old ) ( ( dc_elvalue * )arg )->set_old( true );
      return false;
    }

  }
}

dc_func *parse_func( token_queue &Q,  dc_label *owner, dc_label *search_origin )
{
  token *t = Q.peek();
  if( !t || !t->is_tag() ) return nil;

  /* get function */
  int index = -1;
  parseTag pT = ( ( tag_token * )t )->get();
  if( get_fn_of_type( pT, index, FUNC ) ) {
    return nil;
  }
  Q.pop();

  /* check lparen */
  if( expect_tag( Q, L_Paren_Tag, "parse_func" ) ) {
    return nil;
  }

  dc_func *new_func;
  
  t = Q.peek();
  if( !t ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "parse_func -- unexpected end of stream near " << t->pos_string() 
	   << "\n";
    }
    return nil;
  }
  
  dc_set *set;
  if( ( set = parse_set( Q, owner, search_origin ) ) != nil ) {
    new_func = new dc_tset_op;
    ( ( dc_tset_op * )new_func )->set_set( *set );
    ( ( dc_tset_op * )new_func )->set_info( index );
    // new_func->dep_gen( deps, owner->get_tag() );
  } else {
    bool permanent_set = false;
    if( !t->is_tag() ) {
      string label = ( ( string_token * )t )->get();
      dc_label *id = search( label, search_origin );
      if( id != nil && ( ( id->type() == Element_t && ( ( dc_element * )id )
			   ->get_rtype() == Set_t ) || id->type() == Set_t ) ) {
	new_func = new dc_pset_op( label, search_origin );
	( ( dc_pset_op * )new_func )->set_info( index );
	Q.pop();
	permanent_set = true;
      }
    }

    if( !permanent_set ) {
      new_func = new dc_op;
      ( ( dc_op * )new_func )->set_info( index );
      if( parse_f_list( Q, *( ( dc_op * )new_func ), owner, search_origin ) ) {
	dc_trace( TRACE_ERROR ) {
	cerr << "parse_func -- error parsing argument list near " 
	     << Q.pos_string() << "\n";
	}
	delete( new_func );
	return nil;
      }
    }
  }  

  /* check rparen */
  if( expect_tag( Q, R_Paren_Tag, "parse_func" ) ) {
    delete( new_func );
    return nil;
  }
  return new_func;
}

dc_func *parse_fcall( token_queue &Q,  dc_label *owner, 
		      dc_label *search_origin ) {
  token *t = Q.peek();
  
  /* get label */
  if( !valid_path( t ) ) return nil;
  string label = ( ( string_token * )t )->get();
  
  Q.pop();

  t = Q.peek();
  /* check lparen */
  if( expect_tag( Q, L_Paren_Tag, "parse_fcall" ) ) return nil;

  /* get args */
  dc_func_call *fcall = new dc_func_call( label, search_origin );
  int n = 0;
  if( check_tag( Q, R_Paren_Tag ) ) {

    while( 1 ) {
      /* check for optional label tag */
      t = Q.peek();
      string arg_label;
      if( t && !t->is_tag() ) {
	arg_label = ( ( string_token * )t )->get();
	token *arg_lbl = Q.extract();
	t = Q.peek();
	if( t && *t == Equal_Tag ) {
	  Q.push_used( arg_lbl );
	  Q.pop();
	} else {
	  arg_label = "";
	  Q.push( arg_lbl );
	}
      }
      
      dc_func *f = parse_function( Q, owner, search_origin );

      if( !f ) {
	delete( fcall );
	dc_trace( TRACE_ERROR ) {
	  cerr << "parse_fcall -- error parsing argument " << n << " near "
	       << Q.pos_string() << "\n";
	}
	return nil;
      }
      fcall->add_arg_data( arg_label, *f );

      if( check_tag( Q, Comma_Tag ) ) break;
      n++;
    }

    /* check rparen */
    if( expect_tag( Q, R_Paren_Tag, "parse_fcall" ) ) {
      delete( fcall );
      return nil;
    }
  } else {
    return fcall; /* zero args passed */
  }
  
  /*  if( owner ) {
      tag dep_from = owner->get_tag();
      tag dep_to = e->get_tag();
      deps.append( dep_from ); deps.append( dep_to );
      } */
  return fcall;
}

dc_func *parse_iter_loop( token_queue &Q,  dc_label *owner, 
			  dc_label *search_origin ) {
  if( check_tag( Q, For_Tag ) ) return nil;
  if( expect_tag( Q, L_Paren_Tag, "parse_iter_loop" ) )
    return nil;

  /* check for optional argument type */
  dc_type arg_type;
  token *t = Q.peek();
  if( t->is_tag() && 
      ( arg_type = get_dtype( ( ( tag_token * )t )->get() ) ) != Undef_t ) {
    Q.pop();
  } else {
    arg_type = default_type;
  }

  /* get argument label */
  string label;
  if( expect_label( Q, label, "parse_iter_loop" ) ||
      expect_tag( Q, Comma_Tag, "parse_iter_loop" ) )
    return nil;
  
  dc_iter_loop *iloop = new dc_iter_loop;
  iloop->init_arg( label, arg_type );

  if( !check_label( Q, label ) ) {
    /* MAY WANT TO INSERT CODE HERE TO CHECK FOR EXISTENCE OF MATCHING LABEL
       WITH TYPE DC_ELEMENT->DC_SET OR TYPE DC_SET. */
    // if( fit_search( set_label, find_set_crit , search_origin )

    iloop->set_set( label, search_origin );
  } else {
    dc_set *set;
    if( ( set = parse_set( Q, owner, search_origin ) ) == nil ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "parse_iter_loop -- error parsing set for iteration loop near " 
	     << Q.pos_string() << "\n";
      }
      return nil;
    } else {
      iloop->set_set( *set );
    }
  }

  if( expect_tag( Q, R_Paren_Tag, "parse_iter_loop" ) ||
      expect_tag( Q, L_Bracket_Tag, "parse_iter_loop" ) ) {
    delete( iloop );
    return nil;
  }

  /* get function */
  dc_func *func;
  la_stack_push( iloop->get_arg() );
  if( ( func = parse_function( Q, owner, search_origin ) ) == nil ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "parse_iter_loop -- parse of loop function failed at " 
	   << Q.pos_string() << "\n";
    }
    la_stack_pop( 1 );
    delete( iloop );
    return nil;
  }
  iloop->set_func( *func );
  la_stack_pop( 1 );

  if( expect_tag( Q, R_Bracket_Tag, "parse_iter_loop" ) ) {
    delete( iloop );
    return nil;
  }
  return iloop;
}

bool parse_f_list( token_queue &Q,  dc_op &func, dc_label *owner, 
		   dc_label *search_origin ) {
  /* get first arg */
  dc_func *f = parse_function( Q, owner, search_origin/*, Data_t*/ );
  if( !f ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "parse_f_list -- function expected before " << Q.pos_string()
	   << "\n";
    }
    return true;
  }
  func.add_arg( *f );
  
  while( 1 ) {
    /* check comma */
    if( check_tag( Q, Comma_Tag ) ) return false;

    /* parse next arg */
    dc_func *f = parse_function( Q, owner, search_origin/*, Data_t*/ );
    if( !f ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "parse_f_list -- function expected before " << Q.pos_string()
	     << "\n";
      }
      return true;
    }
    func.add_arg( *f );
  }
}

bool pass_default( token_queue &Q ) {
  string pos = Q.pos_string();
  
  /* check default tag */
  if( check_tag( Q, Default_Tag ) ) return true;

  /* optional data type */
  token *t = Q.peek();  
  if( t->is_tag() ) {
    switch( ( ( tag_token * )t )->get() ) {
    case Boolean_Tag : case Distrib_Tag : case Int_Tag : case Matrix_Tag : 
    case Real_Tag : case String_Tag : case Symbol_Tag : case Triple_Tag : 
    case Vector_Tag : Q.pop();
    default :;
    }
  }

  /* get label */
  t = Q.peek();
  if( !valid_label( t ) ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "pass_default -- could not parse default value label "
	   << "starting at " << pos << "\n";
    }
    return true;
  }
  string label = ( ( string_token * )t )->get();
  Q.pop();
  
  /* check '=' */
  if( expect_tag( Q, Equal_Tag, "pass_default" ) ) {
    return true;
  }

  /* parse value -- pass over value */
  if( preparse_data( Q ) ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "pass default -- could not parse default value definition "
	   << "starting at " << pos << " since value could not be parsed\n";
    }
    return true;
  }

  /* check ';' */
  if( expect_tag( Q, Semicolon_Tag, "pass_default" ) ) {
    return true;
  }
  return false;
}

bool valid_label( token *t ) {
  if( !t || t->is_tag() ) return false;
  return valid_label( ( ( string_token * )t )->get() );
}

bool valid_label( cstring label ) {
  int l = label.length();
  for( int i = 0 ; i < l ; i++ ) {
    char c = label[i];
    if( !isalnum( c ) && !( c == '_' || c == '-' ) ) {
      return false;
    }
  }
  return true;
}

bool valid_path( token *t ) {
  if( !t || t->is_tag() ) return false;
  return valid_path( ( ( string_token * )t )->get() );
}

bool valid_path( cstring label ) {
  int l = label.length();
  for( int i = 0 ; i < l ; i++ ) {
    char c = label[i];
    if( !isalnum( c ) && !( c == '_' || c == '-' || c == ID_SEPARATOR ) ) {
      return false;
    }
  }
  return true;
}

bool parse_global_def( token_queue &Q, dc_component *parent ) {
  token *t = Q.peek();
  if( t && t->is_tag() ) {
   token *tmp_tkn;
    switch( ( ( tag_token * )t )->get() ) {
    case Define_Tag :
      tmp_tkn = Q.extract();
      t = Q.peek();
      Q.push( tmp_tkn );
      if( t ) {
	if( *t == Symbol_Tag ) return pass_symbol_def( Q );
	return parse_typedef( Q );
      }
    case Enum_Tag :
      return pass_enum_def( Q );
    case Default_Tag :
      return pass_default( Q );
    case Link_Tag : 
      return ( parse_link( Q, parent ) == nil );
    default : ;
    }
  }
  return true;
}

bool pass_enum_def( token_queue &Q ) {
  if( expect_tag( Q, Enum_Tag, "pass_enum_def" ) ) return true;

  string enum_str;
  if( expect_label( Q, enum_str, "pass_enum_def" ) ) return true;

  if( expect_tag( Q, L_Bracket_Tag, "pass_enum_def" ) ) return true;
    
  do {
    string symbol_str;
    if( expect_label( Q, symbol_str, "pass_enum_def" ) ) return true;
  } while( !check_tag( Q, Comma_Tag ) );

  if( expect_tag( Q, R_Bracket_Tag, "pass_enum_def" ) ) return true;
  return expect_tag( Q, Semicolon_Tag, "pass_enum_def" );
}

bool pass_symbol_def( token_queue &Q ) {
  if( expect_tag( Q, Define_Tag, "pass_symbol_def" ) ) return true;
  if( expect_tag( Q, Symbol_Tag, "pass_symbol_def" ) ) return true;
  string symbol_str;
  if( expect_label( Q, symbol_str, "pass_symbol_def" ) ) return true;
  return expect_tag( Q, Semicolon_Tag, "pass_symbol_def" );
}

dc_link *parse_link( token_queue &Q, dc_label *owner ) {
  string src_lbl, tgt_lbl;
  bool directed;
  bool targeted;

  string pos = Q.pos_string();

  if( expect_tag( Q, Link_Tag, "parse_link" ) || 
      expect_path( Q, src_lbl, "parse_link" ) ) return nil;

  if( ( directed = !check_tag( Q, Dir_Link_Tag ) ) || 
      !check_tag( Q, Comma_Tag ) ) {
    targeted = true;
    if( expect_path( Q, tgt_lbl, "parse_link" ) ) return nil;
  } else targeted = false;

  triple pta, ptb, ptc, ptd;
  double dist = 0;
  dc_func *fn = nil, *fn_pta = nil, *fn_ptb = nil;
  dc_link_type lt;
  if( !check_tag( Q, Colon_Tag ) ) {
    lt = parse_link_type( Q );
    switch( lt ) {
    case Distance_lt :
      if( parse_triple( Q, pta ) || expect_tag( Q, Comma_Tag, "parse_link" ) ||
	  parse_triple( Q, ptb ) || ( !check_tag( Q, Comma_Tag ) &&
				      parse_real( Q, dist ) ) ) {
	dc_trace( TRACE_ERROR ) {
	  cerr << "parse_link -- error parsing distance link specs near "
	       << Q.pos_string() << "\n";
	}
	return nil;
      }
      break;
    case Axial_lt :
      if( parse_triple( Q, pta ) || expect_tag( Q, Comma_Tag, "parse_link" ) ||
	  parse_triple( Q, ptb ) || expect_tag( Q, Comma_Tag, "parse_link" ) ||
	  parse_triple( Q, ptc ) || expect_tag( Q, Comma_Tag, "parse_link" ) ||
	  parse_triple( Q, ptd ) ) {
	dc_trace( TRACE_ERROR ) {
	  cerr << "parse_link -- error parsing axial link specs near "
	       << Q.pos_string() << "\n";
	}
	return nil;
      }
      break;
    case Force_lt : 
      if( ( fn = parse_function( Q, owner, owner/*, Triple_t*/ ) ) == nil ||
	  expect_tag( Q, Comma_Tag, "parse_link" ) || 
	  ( fn_pta = parse_function( Q, owner, owner/*,Triple_t*/) ) == nil ||
	  ( targeted && ( expect_tag( Q, Comma_Tag, "parse_link" ) || 
	  ( fn_ptb = parse_function( Q, owner, owner/*,Triple_t*/) ) == nil))) {
	dc_trace( TRACE_ERROR ) {
	  cerr << "parse_link -- error parsing specs for force link at "
	       << pos << "\n";
	}
	if( fn ) delete( fn );
	return nil;
      }
      break;
    case Attachment_lt : case Base_lt :
      break;
    default :
      dc_trace( TRACE_ERROR ) {
	cerr << "parse_link -- unsupported link type near " << Q.pos_string()
	     << "\n";
      }
      return nil;
    }
  } else lt = Base_lt;

  if( expect_tag( Q, Semicolon_Tag, "parse_link" ) ) {
    return nil;
  }

  dc_link *new_link;
  if( targeted ) 
    new_link = add_link( src_lbl, tgt_lbl, owner, lt, directed );
  else
    new_link = add_link( src_lbl, owner, lt, directed );

  switch( lt ) {
  case Distance_lt :
    ( ( dc_distance_link * )new_link )->set_pts( pta, ptb, dist );
    break;
  case Axial_lt :
    if( ( ( dc_axial_link * )new_link )->set_pts( pta, ptb, ptc, ptd ) ) {
      dc_trace( TRACE_ERROR) {
	cerr << "parse_link -- axial link at " << pos << " has illegal specs\n";
      }
      delete( new_link );
      return nil;
    }
    break;
  case Force_lt :
    if( directed ) {
      if( ( ( dc_force_link * )new_link )->set_pts( *fn, *fn_pta, *fn_ptb ) ) {
	dc_trace( TRACE_ERROR ) {
	  cerr << "parse_link -- illegal specs for force link at " << pos 
	       << "\n";
	}
	delete( fn );
	delete( fn_pta );
	delete( fn_ptb );
	delete( new_link );
	return nil;
      }
    } else {
      if( ( ( dc_force_link * )new_link )->set_pts( *fn, *fn_pta ) ) {
	dc_trace( TRACE_ERROR ) {
	  cerr << "parse_link -- illegal specs for force link at " << pos 
	       << "\n";
	}
	delete( fn );
	delete( fn_pta );
	delete( new_link );
	return nil;
      }
    }
    break;
  default :;
  }

  /*  if( new_link->rehash() ) {
      dc_trace( TRACE_ERROR ) {
      cerr << "parse_link -- error rehashing link at " << pos << "\n";
      }
      delete( new_link );
      return nil;
      }*/

  return new_link;
}

bool parse_typedef( token_queue &Q ) {
  if( check_tag( Q, Define_Tag ) ) {
    return true;
  }
  
  if( expect_tag( Q, Type_Tag, "parse_typedef" ) ) {
    return true;
  }

  string type_name;
  if( expect_label( Q, type_name, "parse_typedef" ) ) return true;

  user_type u_type;
  if( ( u_type = parse_u_type( type_name ) ) != Undefined_Type ) {
    if( u_type == No_Type ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "parse_typedef -- error redefining " << type_name << " near "
	     << Q.pos_string() << "\n";
      }
      return true;
    }
    dc_trace( TRACE_WARNING ) {
      cerr << "parse_typedef -- type \"" << type_name 
	   << "\" alread exists.  redefinition near " << Q.pos_string() 
	   << " may not affect all previously defined types\n";
    }
  } else {
    u_type = define_u_type( type_name );
  }

  if( !check_tag( Q, Implies_Tag ) ) {
    /* get implied list */
    while( 1 ) {
      token *t = Q.pop();
      user_type implied;
      if( !t || t->is_tag() ) {
	dc_trace( TRACE_ERROR ) {
	  cerr << "parse_typedef -- expected type name but found " << *t
	       << " at " << t->pos_string() << "\n";
	}
	return true;
      }
      string impl_name = ( ( string_token * )t )->get();
      if( ( implied = parse_u_type( impl_name ) ) == Undefined_Type ) {
	dc_trace( TRACE_WARNING ) {
	  cerr << "parse_typedef -- implicit declaration of type " << impl_name
	       << " near " << t->pos_string() << "\n";
	}
	implied = define_u_type( impl_name );
      }
      add_u_type_impl( u_type, implied );
      
      /* check ',' */
      if( check_tag( Q, Comma_Tag ) ) break;
    }
  }

  /* check ';' */
  return expect_tag( Q, Semicolon_Tag, "parse_typedef" );
} 

bool parse_utypes( token_queue &Q, dc_component &target ) {
  if( check_tag( Q, Type_Tag ) ) return false; /* no types need to be parsed */

  do {
    bool inheritance = false;
    if( !check_tag( Q, Inherited_Tag ) ) {
      inheritance = true;
    } else {
      check_tag( Q, Local_Tag );
    }
    
    token *t = Q.pop();
    if( !valid_label( t ) ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "parse_type -- type label near " << t->pos_string() 
	     << " was not a valid label\n";
      }
      return true;
    }
    user_type u_type;
    string u_name = ( ( string_token * )t )->get();
    if( ( u_type = parse_u_type( u_name ) ) == Undefined_Type ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "parse_type -- undefined type " << u_name
	     << " at " << t->pos_string() << "\n";
      }
      return true;
    }
     target.set_u_type( u_type, inheritance );
  } while( !check_tag( Q, Comma_Tag ) );

  return expect_tag( Q, Semicolon_Tag, "parse_type" );
}

dc_finite_set *parse_finite_set( token_queue &Q, dc_label *owner, 
				 dc_label *search_origin ) {
  token *t = Q.peek();

  if( t == nil || !t->is_tag() ) return nil;

  dc_finite_set *set;
  parseTag pT = ( ( tag_token * )t )->get();
  string key = ""; bool key_defined = false;
  user_type u_type = Undefined_Type ; bool type_defined = false;
  string origin_str; bool origin_defined = false;
  if( pT == Descendent_Tag || pT == Type_Tag || pT == Key_Tag ) {
    while( 1 ) {
      if( *t == Key_Tag ) {
	Q.pop();
	t = Q.peek();
	if( !t || t->is_tag() ) {
	  dc_trace( TRACE_ERROR ) {
	    cerr << "parse_finite_set -- expected pattern at " 
		 << t->pos_string() << " but found " << *t << "\n";
	    return nil;
	  }
	}
	key = ( ( string_token * )t )->get();
	dequote( key );
	if( key_defined ) {
	  dc_trace( TRACE_WARNING ) {
	    cerr << "parse_finite_set -- redefining key at " 
		 << t->pos_string() << "\n";
	  }
	} else {
	  key_defined = true;
	}
	Q.pop();
      } else if( *t == Type_Tag ) {
	Q.pop();
	t = Q.peek();
	if( valid_label( t ) ) {
	  string type_name = ( ( string_token * )t )->get();
	  user_type ut = parse_u_type( type_name );
	  if( ut == Undefined_Type ) {
	    dc_trace( TRACE_ERROR ) {
	      cerr << "parse_finite_set -- undefined type \"" 
		   << type_name << "\" at " << t->pos_string() << "\n";
	    }
	    return nil;
	  }
	  Q.pop();
	  u_type = ut;
	  if( type_defined ) {
	    dc_trace( TRACE_WARNING ) {
	      cerr << "parse_finite_set -- redefining type at " 
		   << t->pos_string() << "\n";
	    }
	  } else {
	    type_defined = true;
	  }
	} else {
	  dc_trace( TRACE_ERROR ) {
	    cerr << "parse_finite_set -- error parsing type at " 
		 << t->pos_string() << "\n";;
	  }
	  return nil;
	}
      } else {
	if( *t == Descendent_Tag ) {
	  Q.pop(); check_tag( Q, Of_Tag );
	  t = Q.peek();
	}
	if( valid_path( t ) ) {
	  origin_str = ( ( string_token * )t )->get();

	  Q.pop();
	  if( origin_defined ) {
	    dc_trace( TRACE_WARNING ) {
	      cerr << "parse_finite_set -- redefining origin at " 
		   << t->pos_string() << "\n";
	    }
	  } else {
	    origin_defined = true;
	  }
	} else {
	  dc_trace( TRACE_ERROR ) {
	    cerr << "parse_finite_set -- error parsing set at " 
		 << t->pos_string() << "\n";;
	  }
	  return nil;
	}
      }
      if( check_tag( Q, Of_Tag ) && check_tag( Q, Comma_Tag ) ) break;
      t = Q.peek();
      if( !t ) {
	dc_trace( TRACE_ERROR ) {
	  cerr << "parse_finite_set -- premature end of file\n";
	}
	return nil;
      }
    }

    set = new dc_desc_set( key, u_type, origin_str, search_origin );
  } else if( pT == Set_Tag ) {
    Q.pop();
    if( expect_tag( Q, L_Paren_Tag, "parse_finite_set" ) ) return nil;
    set = new dc_label_set;
    do {
      string label;
      if( expect_path( Q, label, "parse_finite_set"  ) ) {
	delete( set );
	return nil;
      }
      /* dc_label *id = search( label, search_origin );
	 if( !id ) {
	 dc_trace( TRACE_ERROR ) {
	 cerr << "parse_finite_set -- undefined label \"" << label 
	 << "\" near " << t->pos_string() << "\n";
	 return nil;
	 }
	 } */
      ( ( dc_label_set * )set )->add( label );
    } while( !check_tag( Q, Comma_Tag ) );
    
    if( expect_tag( Q, R_Paren_Tag, "parse_finite_set" ) ) {
      delete( set ); 
      return nil;
    }
  /* } else if( pT == All_Tag ) {
     Q.pop();
     set = new dc_all_set();
     } else if( pT == None_Tag ) {
     Q.pop();
     set = new dc_all_set(); */
  } else if( pT == Self_Tag ) {
    Q.pop();
    set = new dc_list_set();
    ( ( dc_list_set * )set )->add( *owner );
  } else return nil;

  return set;
}

dc_set *parse_set( token_queue &Q, dc_label *owner, dc_label *search_origin ) {
  dc_finite_set *fset = parse_finite_set( Q, owner, search_origin );

  if( fset == nil ) return nil;
  dc_set *set = new dc_set();
  set->add_set( *fset );

  while( !check_tag( Q, Ampersand_Tag ) ) {
    if( ( fset = parse_finite_set( Q, owner, search_origin ) ) == nil ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "parse_set -- error near " << Q.pos_string() << "\n";
      }
      delete( set );
      return nil;
    }
    set->add_set( *fset );
  }
  
  if( !check_tag( Q, Exception_Tag ) ) {
    do {
      if( ( fset = parse_finite_set( Q, owner, search_origin ) ) == nil ) {
	dc_trace( TRACE_ERROR ) {
	  cerr << "parse_set -- error near " << Q.pos_string() << "\n";
	}
	delete( set );
	return nil;
      }
      set->add_exception( *fset );
    } while( !check_tag( Q, Ampersand_Tag ) );
  }

  /* except self if necessary */
  set->rehash();
  if( owner && set->member( *owner ) ) {
    dc_trace( TRACE_MANY ) {
      cerr << "parse_set -- excluding self from set ending before " 
	   << Q.pos_string() << "\n";
    }
    dc_list_set *lset = new dc_list_set;
    lset->add( *owner );
    set->add_exception( *( ( dc_finite_set * )lset ) );
  }

  return set;
}

/* included to allow lists of parseTags to be kept by LEDA */
int compare( parseTag const &a, parseTag const &b ) {
  return ( ( int )&a ) - ( ( int )&b );
}

string dtype_string( dc_type T ) {
  return string( dc_type_string[T] );
}
