// file: $isip/class/asr/JSGFToken/jt_05.cc
//
// implementation file for Token class
//
// isip include files
//
#include "JSGFToken.h"
#include <Console.h>

//-------------------------------------------------------------------
//
// private methods
//
//--------------------------------------------------------------------

// method: isHeader
// check if an input token is a grammar header
//
boolean JSGFToken::isHeader(const String& arg_a) {

  // if starting with a # sign and ending with semi-colon,
  // the input string is a header
  //
  if ((arg_a(0) != '#') || (arg_a(arg_a.length() - 1) != ';')) {
    return false;
  }
  return true;	
}

// method: isKeyword
// check if a input string is a keyword "public"
//
boolean JSGFToken::isKeyword(const String& arg_a) {
  
  if(arg_a.eq(L"public", true)) {
    return true;
  }
  else {
    return false;
  }
}

// method: isGrammarName
// check if an input string is a grammar name
//
boolean JSGFToken::isGrammarName(const String& arg_a) {

  String head, gram_name;
  long len = arg_a.length();
  long start = arg_a.firstNotSpace(7);
  long end = arg_a.lastNotSpace(len - 2);

  arg_a.substr(head, 0, 7);  
  arg_a.substr(gram_name, start, end - start + 1);

  // make sure the heading word is "grammar" and the ending is semi-colon
  //
  if ((!head.eq(L"grammar", true)) ||
      (arg_a(arg_a.length() - 1) != ';')) {
    return false;
  }

  // the dot cannot be the first or the last character in the name
  //
  if ((gram_name(0) == '.') ||
      (gram_name(gram_name.length() - 1) == '.')) {
    return false;
  }
  
  return true;
}

// method: isImportGrammar
// check if an input string is an import grammar
//
boolean JSGFToken::isImportGrammar(const String& arg_a) {

  String head, imported_name;
  long len = arg_a.length();
  long start = arg_a.firstNotSpace(6);
  long end = arg_a.lastNotSpace(len - 2);
  
  arg_a.substr(head, 0, 6);
  arg_a.substr(imported_name, start, end - start + 1);
  
  // make sure the heading word is "import" and the ending is semi-colon
  //
  if ((!head.eq(L"import", true)) ||
      (arg_a(len - 1) != ';')) {
    return false;
  }

  // make sure the imported name is surrounded by < and > signs
  //
  if ((imported_name(0) != '<') ||
      (imported_name(imported_name.length() - 1) != '>')) {
    return false;
  }

  // make sure the imported name is a dot-separated name
  //
  long dot_counter = 0;
  for (long i = 1; i < imported_name.length() - 1; i++) {
    if (imported_name(i) == '.' ) {
      dot_counter++;
    }
  }
  if (dot_counter == 0) {
    return false;
  }

  // the dot cannot be the first or the last character in the name
  //
  if ((imported_name(1) == '.') ||
      (imported_name(imported_name.length() - 2) == '.')) {
    return false;
  }
  
  return true;
}

// method: isRuleName
// check if an input string is a rule name
//
boolean JSGFToken::isRulename(const String& arg_a) {

  // if the input string is surrounded by <>
  // the input string is a rule defination
  //
  Char begin = arg_a(0);
  Char end = arg_a(arg_a.length()-1);
  if((begin != '<') || (end != '>')) {
    return false;
  }

  // make sure no whitespace is in the rulename
  //
  for (long i = 1; i < arg_a.length() - 1; i++) {

    Char ch(arg_a(i));
    if (ch.isSpace()) {
      return false;
    }
  }
  
  // then the input is a legal rulename
  //
  return true;
}

// method: isTerminal
// check if an input string is a terminal symbol (JSGF "token")
//
boolean JSGFToken::isTerminal(const String& arg_a) {
  
  // make sure any character in the string is legal for a terminal token
  //
  for (long i = 0; i < arg_a.length(); i++) {
  
    Char ch = arg_a(i);
    if ((ch.isSpace()) || (ch == '"') || (ch == ';')
	|| (ch == '=') || (ch == '|') || (ch == '*')
	|| (ch == '+') || (ch == '<') || (ch == '>')
	|| (ch == '(') || (ch == ')') || (ch == '[')
	|| (ch == ']') || (ch == '{') || (ch == '}')
	|| (ch == '/')) {
      
      return false;
    }
  }
  return true;
}

// method: isOperator
// check if a input string is one of following operators
// =       assign a rule expansion to a rulename
// ;       terminate a rule, a header, or a grammar decalration
// |       alternative
// *       unary, zero or more times
// +       unary, one or more times 
// (       start grouping 
// )       end grouping 
// [       start optional grouping 
// ]       end optional grouping
//
// @@      self-defined queue marker to indicate self_loop rule extension
// ^^      self-defined stack marker to indicate "|" relation of terminals
//
boolean JSGFToken::isOperator(const String& arg_a)
{
  String tmp = arg_a;
  if(tmp.eq(L"=", true) || tmp.eq(L"|", true) || tmp.eq(L";", true) ||
     tmp.eq(L"*", true) || tmp.eq(L"+", true) ||
     tmp.eq(L"(", true) || tmp.eq(L")", true) ||
     tmp.eq(L"[", true) || tmp.eq(L"]", true)) {
     
    return true;
  }
  else {
    return false;
  }
}

// method: isTag
// check if a input string is a tag
//
boolean JSGFToken::isTag(const String& arg_a) {

  // check if the input string is delimited by curly brace {}
  //
  Char begin = arg_a(0);
  Char end = arg_a(arg_a.length() - 1);

  if(!((begin == '{') && (end == '}'))) {
    return false;
  }
  return true;
}

// method: isWeight
// check if a input string is a weight of the token
//
boolean JSGFToken::isWeight(const String& arg_a) {
  
  // check if there are slashes starting and ending the input string
  //
  Char begin = arg_a(0);
  Char end = arg_a(arg_a.length() - 1);

  if(!((begin == '/') && (end == '/'))) {
    return false;
  }
 
  // variables
  //
  String weight;
  String delim(L"/ ");
  long pos = 0;
  boolean dot = false;
  
  // make sure exact one weight value exist and no space in the weight value
  //
  if (arg_a.countTokens(delim) != 1) {
    return false;
  }
  
  // cut away white space and the slash delimiters and
  // get the sub-string for the value
  //
  arg_a.tokenize(weight, pos, delim);
  
  for (long i = 0; i < weight.length(); i++) {
       
    // check if the character is a legal part of float or integer number
    //
    Char ch = weight(i);

    // check the first character separately since it can be '+' or '-'
    //
    if (i == 0 ) {
      if (ch.isDigit() || (ch == '.') || (ch == '+') || (ch == '-' )) {
	if (ch == '.') {
	  dot = true;
	}
      }
      else {
	return false;
      }
    }
    // then check the others
    //
    else {
      if (ch.isDigit() || ((ch == '.') && (!dot))) {
	if (ch == '.') {
	  dot = true;
	}
      }
      else {
	return false;
      }
    }
  } // end: for (long i = 0; i < weight.length(); i++)

  // gracefully exit
  //
  return true;
}

// method: isQuotedToken
// check if an input string is a JSGF quoted token
//
boolean JSGFToken::isQuotedToken(const String& arg_a) {

  long len = arg_a.length();
  
  // make sure the input string starting and ending with " sign
  //
  if ((arg_a(0) != '"') || (arg_a(len - 1) != '"')) {
    return false;
  }
    
  // make sure any character is legal in a quoted token
  //
  boolean empty = true;
  for (long i = 1; i < len - 1; i++) {  
    Char ch = arg_a(i);
    if (!ch.isSpace()) {
      empty = false;
    }
  }

  // make sure between the quote symbols is not empty
  //
  if (empty) {
    return false;
  }  
  return true;
}
