/* func_parse.cc */

#include "parser.h"

/* this file includes tree structure needed to parse functions
   tree nodes are dc_ops
   nodes are dc_funcs

   algorithm
   parse first arg
   parse operator
   create dc_op operation for this node
   if operator not legal operation
      return first arg
   while( 1 )
      parse arg
      parse next_op
      if( next_op < op ) -- next op has higher precedence
        create a lower node
        set next op as op
      else if( next_op > op ) -- next op has lower precedence
         move up in tree
      else add to arg list
   */


/* destructors delete function but on resolve all set to nil */
struct fparse_node;

class fparse_leaf {
public:
  fparse_node *parent;
  dc_func *f;

  fparse_leaf( dc_func *F = nil ) { parent = nil; f = F; }
  virtual ~fparse_leaf( void ) { if( f ) delete( f ); }
  virtual dc_func *resolve( void );
};

class fparse_node : public fparse_leaf {
  parseTag local_op;
  fn op;
  t_fn t_op;
  list<fparse_leaf *> children;
  
public:
  fparse_node( parseTag op ) { local_op = op; }
  ~fparse_node( void );
  fparse_node *add_op( dc_func *arg, parseTag op );
  dc_func *resolve( void );
};

dc_func *parse_function( token_queue &Q, dc_label *owner, dc_label *srch_origin,
			 /*dc_type rtype = Data_t,*/dc_func *first_arg = nil ) {
  if( first_arg == nil )
    if( parse_arg( Q, first_arg, owner, srch_origin ) ) return nil;
  
  token *t = Q.peek();
  if( !t || !t->is_tag() ) {
    return fn_postparse( first_arg/*, rtype*/ );
  }
  parseTag op = ( ( tag_token * )t )->get();  
  if( !is_fn_of_type( op, OP ) ) {
    return fn_postparse( first_arg/*, rtype*/ );
  }

  fparse_node *current = new fparse_node( op );
  current->add_op( first_arg, op );

  Q.pop();

  while( 1 ) {
    dc_func *arg;
    if( parse_arg( Q, arg, owner, srch_origin/*, Data_t*/ ) ) {
      while( current->parent ) {
	current = current->parent;
      }
      delete( current );
      return nil;
    }

    token *t = Q.peek();
    if( !t || !t->is_tag() ) {
      current = current->add_op( arg, op );
      break;
    }
    parseTag new_op = ( ( tag_token * )t )->get();

    if( !is_fn_of_type( new_op, OP ) ) {
      current = current->add_op( arg, op );
      break;
    }
    
    op = new_op;
    current = current->add_op( arg, op );
    Q.pop();   
  }
  while( current->parent ) {
    current = current->parent;
  }
  
  dc_func *resolved = current->resolve();
  delete( current );
  if( !resolved ) return nil;

  return fn_postparse( resolved/*, rtype*/ );
}

dc_func *fparse_leaf::resolve( void ) {
  dc_func *g = f;
  f = nil;
  return g;
}

fparse_node::~fparse_node( void ) {
  if( f ) delete( f );
  fparse_leaf *child;
  forall( child, children ) {
    delete( child );
  }
  children.clear();
}

fparse_node *fparse_node::add_op( dc_func *arg, parseTag op ) {
  if( op < local_op ) { /* higher priority -- move down in tree */
    fparse_node *new_fpn = new fparse_node( op );
    children.append( new_fpn );
    new_fpn->parent = this;
    return new_fpn->add_op( arg, op );
  } else if( op > local_op ) { /* move up */
    fparse_leaf *child = new fparse_leaf( arg );
    children.append( child );
    child->parent = this;
    if( !parent ) {
      parent = new fparse_node( op );
      parent->children.append( this );
    } else {
      fparse_node *t = this;
      while( t->parent && ( op > t->parent->local_op ) ) {
	t = t->parent;
      }
      if( !t->parent ) {
	t->parent = new fparse_node( op );
	t->parent->children.append( t );
	return t->parent;
      }
      if( op == t->parent->local_op )
	return t->parent;
      /* need to inser another layer between t and t->parent */
      /* create new node with parent t->parent */
      fparse_node *new_fpn = new fparse_node( op );
      new_fpn->parent = t->parent;
      /* remove t from t->parent */
      list_item li;
      li = t->parent->children.search( t );
      t->parent->children.assign( li, new_fpn );
      /* add t as child of new_node */
      new_fpn->children.append( t );
      t->parent = new_fpn;
      return new_fpn;
    }
    return parent;
  } else {
    fparse_leaf *child = new fparse_leaf( arg );
    children.append( child );
    child->parent = this;
  }
  return this;
}

dc_func *fparse_node::resolve( void ) {
  int index = -1;
  get_fn_of_type( local_op, index, OP );
  if( f ) delete( f );
  f = ( dc_func * )new dc_op();
  ( ( dc_op * )f )->set_info( index );
  fparse_leaf *child;
  forall( child, children ) {
    dc_func *arg = child->resolve();
    ( ( dc_op * )f )->add_arg( *arg );
  }
  dc_func *g = f;
  f = nil;
  return g;
}

dc_func *fn_postparse( dc_func *f/*, dc_type*/ ) {
  if( !f ) return nil;

  dc_data *d = f->simplify();
  if( d ) {
    delete( f );
    return new dc_const( *d );
  }
  return f;
}

int compare( fparse_leaf *const &a, fparse_leaf *const &b ) {
  return ( long int )a - ( long int )b;
}

