// file: $isip/class/asr/JSGFParser/jp_08.cc
// version: $Id: jp_08.cc 10499 2006-03-15 21:43:17Z may $
//
// isip include files
//
#include "JSGFParser.h"

// method: replaceRuleReference
//
// arguments: JSGFToken& rulename_a: (inoput) a given rule reference name
//            Stack<String> replaced_rules_a: (input) replaced rule vector
// return: a boolean value indicating status
//
// This method takes a JSGF rule expansion and recursively replace all
// the rule references with the actual matching rules until only terminal
// symbols can be found in the rule expansion without any reference.
//
boolean JSGFParser::replaceRuleReference(JSGFToken& rulename_a,
					 Stack<String>& replaced_rules_a) {

  boolean is_processed = false;
 
  processed_rules_d.concat(rulename_a);
  
  // store the input rulename in order to be checked for
  // the potential right recursion 
  //
  replaced_rules_a.push(&rulename_a.rulename_d);
  
  // get the corresponding rule expansion of the input rulename
  //
  Vector<JSGFToken> rule;
  resolveReference(rulename_a.rulename_d, rule);
  
  GraphVertex<String>* curr_vert;

  long index = 0;

  //  boolean is_graph_term = false;
  String term_rule_name;
  term_rule_name.assign(rulename_a.rulename_d);

  // loop through the rule expansion and replace all rule references
  //  
  while (index < rule.length()) {

    JSGFToken current = rule(index);
    
    // add all non-rulename token into the class-protected final token queue
    //
    if (current.getTokenType() != JSGFToken::RULE_NAME) {

      // setting the index of vertex in corresponding vertex list of graph
      //
      if ((current.getTokenType() == JSGFToken::TERMINAL ||
	   current.getTokenType() == JSGFToken::QUOTED_TOKEN)
	  && current.vertex_index_d == -1) {

	// if the token is the reserved ISIP graph starting or ending symbol
	// set the vertex as the default graph start or term accordingly 
	//
	if (current.terminal_d.eq(graph_start_d)) {
	  curr_vert = graph_d.getStart();
	}
	else if (current.terminal_d.eq(graph_term_d)) {
	  curr_vert = graph_d.getTerm();
	}
	// otherwise, create a vertex for the current token
	//
	else {
	  curr_vert = graph_d.insertVertex(&current.terminal_d);
	  boolean isExist = false;
	  for(int i = 0; i < symbol_list_d.length(); i++) {
	    if(symbol_list_d(i).eq(current.terminal_d)) {
	      isExist = true;
	      break;
	    }
	  }
	  if(!isExist)
	    symbol_list_d.concat(current.terminal_d);
	}
		
	// store this vertex into the vertex list
	//
	vertex_list_d[vertex_list_index_d] = curr_vert;
	current.setTermRuleName(term_rule_name);
	current.setVertexIndex(vertex_list_index_d);
	setVertexIndex(current);
	vertex_list_index_d++;
      }
      else if (current.operator_d(0).eq(L")")) {
	if(replaced_rules_a.contains(&current.term_rule_name_d)) {
	  String tmp1;
          String * tmp2;
	  replaced_rules_a.pop(&tmp1);
	  tmp2 = replaced_rules_a.peek();
	  term_rule_name.assign(*tmp2);
	}
      }
      final_d.add(&current);   
    }
    
    // else, the token is a rulename suggesting a rule reference
    //
    else {
      
      // first check if a right recursion is formed with
      // the current rule reference
      //
      boolean recursion = false;

      // if the current rule reference forms a right recursion to
      // a previously processed rule
      //
      if(replaced_rules_a.contains(&current.rulename_d)) {

	// create a marker "@@" token to indicate this right recursion
	//
	JSGFToken recur_marker;
	recur_marker.setMarker(L"@@", current);
	
	// add it into the final queue
	//
	final_d.add(&recur_marker);
	//col_index++;
	
	// update the boolean status
	//
	recursion = true;
      }
      
      // if no right recursion is found, the current rule reference needs
      // to be replaced by its corresponding rule expansion
      //
      if (!recursion) {
	
	// add a "(" left brace for starting a rule extension
	//
	JSGFToken left, right;
	Vector<JSGFToken> head, tail;

	for(long i = index + 1; i < rule.length(); i++) {
	  tail.concat(rule(i));
	}

	left.setToken(L"(");
	head.concat(left);
	index = 0;
	final_d.add(&left);
        is_processed = false;
	
	// if the current rule is a previously processed rule
	//
	for (long i = 0; i < processed_rules_d.length(); i++) {
	  
	  if(current.rulename_d.eq(processed_rules_d(i).rulename_d)) {
	    is_processed = true;
	  }
	}
  
	// store the input rulename in order to be checked for unique terminals
	//
	if(!is_processed) 
	  processed_rules_d.concat(current);
	
	replaced_rules_a.push(&current.rulename_d);
	
	// get the corresponding rule expansion of the input rulename
	//
	Vector<JSGFToken> rule1;
	resolveReference(current.rulename_d, rule1);
	head.concat(rule1);
       	term_rule_name.assign(current.rulename_d);
	
	// add a ")" close brace for closing the rule extension
	//
	right.setToken(L")");
	right.setTermRuleName(current.rulename_d);
	head.concat(right);
	rule.clear();
	rule.concat(head);
	rule.concat(tail);
      }
    } // end: if (current.getTokenType() != 4) else structure
    index++;
  } // end: while(index < rule.length())

  // gracefully exit
  //
  return true;
}

// method: resolveReference
//
// arguments:
//    const JSGFToken& rulename_a: (inoput) a given rule reference name
//    Vector<JSGFToken>& rule_exp_a: (output) a rule expansion corresponding
//                                     to the input rulename
//
// return: a boolean value indicating status
//
// This method checks out a rule expansion from the rule tables according to
// the input rulename
//
boolean JSGFParser::resolveReference(String& rulename_a,
				     Vector<JSGFToken>& rule_exp_a) {

  // boolean variable to check if the rule expansion is found
  //
  boolean found = false;
  
  // pick up the rule expansion from the private rule table according to
  // the input rulename
  //
  for (long i = 0; i < private_rule_table_d.length(); i++) {

    JSGFToken t = private_rule_table_d(i).first();

    // copy the rule expansion if the rulename is found
    //
    if (rulename_a.eq(t.rulename_d)) {      
      rule_exp_a = private_rule_table_d(i).second();
      found = true;
      break;
    }
  }
  
  // if the rule expansion cannot be found in the private rule table,
  // search it in the public rule table
  //
  if (!found) {
    for (long i = 0; i < public_rule_table_d.length(); i++) {
      
      JSGFToken t = public_rule_table_d(i).first();
      
      // copy the rule expansion if the rulename is found
      //
      if (rulename_a.eq(t.rulename_d)) {      
	rule_exp_a = public_rule_table_d(i).second();
	found = true;
	break;
      }
    }
  } // end: if (!found)

  // error if the rule expansion cannot be found in either of the rule tables
  //
  if (!found) {
    String output(L"referred rule <");
    output.concat(rulename_a);
    output.concat(L"> not found");
    return Error::handle(name(), output, Error::TEST, __FILE__, __LINE__);
  }

  // gracefully exit
  //
  return true;  
}


// method: setVertexIndex
//
// arguments:
//    JSGFToken& token_a: (input) a given token
//
// return: a boolean value indicating status
//
// This method set the index of graph vertex list for corresponding terminal
// token in the rule
//
boolean JSGFParser::setVertexIndex(JSGFToken& token_a) {

  // boolean variable to check if the rule expansion is found
  //
  boolean found = false;

  // a rule expansion corresponding to the rulename
  //
  Vector<JSGFToken> rule_exp;
	
  // pick up the rule expansion from the private rule table according to
  // the input rulename
  //
  for (long i = 0; i < private_rule_table_d.length(); i++) {

    JSGFToken t = private_rule_table_d(i).first();

    // copy the rule expansion if the rulename is found
    //
    if (token_a.term_rule_name_d.eq(t.rulename_d)) {      
      rule_exp = private_rule_table_d(i).second();
      for(long j = 0; j < rule_exp.length(); j++) {
	if(token_a.getTokenType() == rule_exp(j).getTokenType() && token_a.terminal_d.eq(rule_exp(j).terminal_d) && rule_exp(j).vertex_index_d == -1) {
	  rule_exp(j).setVertexIndex(vertex_list_index_d);
	  rule_exp(j).setTermRuleName(t.rulename_d);
	  break;
	}
      }
      
      // create a pair of rule name and rule expansion
      //
      Pair< JSGFToken, Vector<JSGFToken> > pair;
      pair.assign(t, rule_exp);
      private_rule_table_d(i).assign(pair);
            
      found = true;
      break;
    }
  }
  
  // if the rule expansion cannot be found in the private rule table,
  // search it in the public rule table
  //
  if (!found) {
    for (long i = 0; i < public_rule_table_d.length(); i++) {
      
      JSGFToken t = public_rule_table_d(i).first();
      
      // copy the rule expansion if the rulename is found
      //
      if (token_a.term_rule_name_d.eq(t.rulename_d)) {      
	rule_exp = public_rule_table_d(i).second();
	for(long j = 0; j < rule_exp.length(); j++) {
	  if(token_a.getTokenType() == rule_exp(j).getTokenType() && token_a.terminal_d.eq(rule_exp(j).terminal_d) && rule_exp(j).vertex_index_d == -1) {
	    rule_exp(j).setVertexIndex(vertex_list_index_d);
	    rule_exp(j).setTermRuleName(t.rulename_d);
	    break;
	  }
	}
	
	// create a pair of rule name and rule expansion
	//
	Pair< JSGFToken, Vector<JSGFToken> > pair;
	pair.assign(t, rule_exp);
	public_rule_table_d(i).assign(pair);
		
	found = true;
	break;
      }
    }
  } // end: if (!found)

  // error if the rule expansion cannot be found in either of the rule tables
  //
  if (!found) {
    String output(L"referred rule <");
    output.concat(token_a.term_rule_name_d);
    output.concat(L"> not found");
    return Error::handle(name(), output, Error::TEST, __FILE__, __LINE__);
  }

  // gracefully exit
  //
  return true;  
}

// method: replaceRecursionRuleReference
//
// arguments: String& rulename_a: (input) a given rule reference name
//            Stack<JSGFToken> replaced_rules_a: (input) replaced rule vector
// return: a boolean value indicating status
//
// This method takes a JSGF rule expansion and recursively replace all
// the rule references with the actual matching rules until only terminal
// symbols can be found in the rule expansion without any reference.
//
boolean JSGFParser::replaceRecursionRuleReference(String& rulename_a,
					    Stack<String> replaced_rules_a,
					    Queue<JSGFToken>& rule_final_a) {

  // store the input rulename in order to be checked for
  // the potential right recursion 
  //
  replaced_rules_a.push(&rulename_a);
  
  // get the corresponding rule expansion of the input rulename
  //
  Vector<JSGFToken> rule;
  resolveReference(rulename_a, rule);

  long index = 0;

  String term_rule_name;
  term_rule_name.assign(rulename_a);

  // loop through the rule expansion and replace all rule references
  //  
  while (index < rule.length()) {

    JSGFToken current = rule(index);
    
    // add all non-rulename token into the class-protected final token queue
    //
    if (current.getTokenType() != JSGFToken::RULE_NAME) {      
      rule_final_a.add(&current);
    }
    
    // else, the token is a rulename suggesting a rule reference
    //
    else {
      // first check if a right recursion is formed with
      // the current rule reference
      //
      boolean recursion = false;

      // if the current rule reference forms a right recursion to
      // a previously processed rule
   
      if(replaced_rules_a.contains(&current.rulename_d)) {

	// create a marker "@@" token to indicate this right recursion
	//
	JSGFToken recur_marker;
	recur_marker.setMarker(L"@@", current);
	
	// add it into the final queue
	//
	rule_final_a.add(&recur_marker);
	
	// update the boolean status
	//
	recursion = true;
	
      }
      
      // if no right recursion is found, the current rule reference needs
      // to be replaced by its corresponding rule expansion
      //
      if (!recursion) {
	
	// add a "(" left brace for starting a rule extension
	//
	JSGFToken left, right;
	Vector<JSGFToken> head, tail;
	for(long i = index + 1; i < rule.length(); i++) {
	  tail.concat(rule(i));
	}
	left.setToken(L"(");
	head.concat(left);
	index = 0;
	rule_final_a.add(&left);
	
	replaced_rules_a.push(&current.rulename_d);
	
	// get the corresponding rule expansion of the input rulename
	//
	Vector<JSGFToken> rule1;
	resolveReference(current.rulename_d, rule1);
	head.concat(rule1);
       	term_rule_name.assign(current.rulename_d);

	// add a ")" close brace for closing the rule extension
	//
	right.setToken(L")");
	right.setTermRuleName(current.rulename_d);
	head.concat(right);
	rule.clear();
	rule.concat(head);
	rule.concat(tail);	
      }
    } // end: if (current.getTokenType() != 4) else structure
    index++;
  } // end: while(index < rule.length())

  // gracefully exit
  //
  return true;
}







