MudOSa4DGD/
MudOSa4DGD/bin/
MudOSa4DGD/data/
MudOSa4DGD/doc/
MudOSa4DGD/doc/driver/
MudOSa4DGD/doc/efun/bitstrings/
MudOSa4DGD/doc/efun/command/
MudOSa4DGD/doc/efun/communication/
MudOSa4DGD/doc/efun/heart_beat/
MudOSa4DGD/doc/efun/interactive/
MudOSa4DGD/doc/efun/inventory/
MudOSa4DGD/doc/efun/living/
MudOSa4DGD/doc/efun/mappings/
MudOSa4DGD/doc/efun/strings/
MudOSa4DGD/doc/efun/uid/
MudOSa4DGD/doc/funs/
MudOSa4DGD/doc/language/
MudOSa4DGD/mudlib/dgd/doc/
MudOSa4DGD/mudlib/dgd/lib/include/dgd/
MudOSa4DGD/mudlib/dgd/lib/std/
MudOSa4DGD/mudlib/dgd/lib/sys/
MudOSa4DGD/mudlib/dgd/log/
MudOSa4DGD/mudlib/log/
MudOSa4DGD/mudlib/std/include/
MudOSa4DGD/mudlib/std/obj/
/*
 * grammar.c
 *
 * SFUN: grammar sfuns collection
 *
 * Taken from VikingMUD.
 * Modified by Frank Schmidt for DGD.
 *
 */


#ifdef __AUTO   /* in AUTO object */


/* path to object holding grammar variables and functions */
#define GRAMMAR_OB     SIMUL_EFUN



/* maks says how big number before we get "lots of"-string returned */
static varargs string convert_int(int n, int maks) {
  return GRAMMAR_OB->convert_int(n, maks);
}

static mixed convert_string_to_int(string s) {
  return GRAMMAR_OB->convert_string_to_int(s);
}

static string convert_int_to_ordinal(int n) {
  return GRAMMAR_OB->convert_int_to_ordinal(n);
}

static string convert_int_to_ordinal_number(int n) {
  return GRAMMAR_OB->convert_int_to_ordinal_number(n);
}

static mixed convert_ordinal_string_to_int(string s) {
  return GRAMMAR_OB->convert_ordinal_string_to_int(s);
}

static string pluralize_word(string str) {
  return GRAMMAR_OB->pluralize_word(str);
}

static string singularize_word(string word) {
  return GRAMMAR_OB->singularize_word(word);
}

static string add_article(string noun) {
  return GRAMMAR_OB->add_article(noun);
}

static string get_article(string noun) {
  return GRAMMAR_OB->get_article(noun);
}

/* add article only if it can be done */
static varargs string maybe_add_article(string noun, int flag) {
  int sz;
  string tmp;

  tmp = tolower(noun);
  sz = strlen(tmp);
  if ((sz > 1) && (tmp[0..1]     == "a "       /* covers 'a few ' and 'a lot ' too.. */
      || (sz > 2) && (tmp[0..2]  == "an "
      || (sz > 3) && ((tmp[0..3] == "the "
		    || tmp[0..3] == "one ")
      || (sz > 4) && (tmp[0..4]  == "some ")))))
    return noun;
  else if (!flag)
    return GRAMMAR_OB->add_article(noun);
  else
    return "the "+noun;
}

/* remove article only if it can be done */
static string remove_article(string noun) {
  int i, sz;
  string tmp;

  i = 0;
  tmp = tolower(noun);
  sz = strlen(tmp)-1;
  if (sz > 4 && (tmp[0..5] == "a few "))
    i = 6;
  else if (sz > 7 && (tmp[0..8]  == "a lot of "))
    i = 9;
  else if (sz > 0) {
    if (tmp[0..1]        == "a ")
      i = 2;
    else if (sz > 1) {
      if (tmp[0..2]      == "an ")
	i = 3;
      else if (sz > 2) {
	if (tmp[0..3]    == "the " || tmp[0..3] == "one ")
	  i = 4;
	else if (sz > 3) {
	  if (tmp[0..4]  == "some ")
	    i = 5;
	}
      }
    }
  }
  /* return noun with extra articles removed (hopefully) */
  if (i <= sz)
    return noun[i..sz];
  return "";
}


/* give a list of names */
static varargs string composite_list(string *arr, string bind) {
  int sz;
  string ret;

  /* default value */
  if (!bind) bind = " and ";

  /* implode into "X, Y and Z". */
  if ((sz=::sizeof(arr)) > 2) {
    ret = implode(arr[..sz-2], ", ");
    ret += bind+arr[sz-1];
  }
  else { /* 2 or less elements */
    ret = implode(arr, bind);
  }

  /* return the list like: "apples, peas and bananas" */
  return ret;
}


/* singularize a word with article */
static string singularize(string noun) {
  return GRAMMAR_OB->singularize_word(remove_article(noun));
}


static string pluralize(string sent) {
  string *words;
  int i;
  words = big_explode(sent, " ");
  if (sizeof(words) == 1)
    return pluralize_word(words[0]);
  if ((i = member_array("of", words)) > 0)
    words[i - 1] = pluralize_word(words[i - 1]);
  else
    words[sizeof(words) - 1] = pluralize_word(words[sizeof(words) - 1]);
  return big_implode(words, " ");
}

static varargs string composite_short(string desc, int number, int numeric) {
  if (stringp(desc)) {
    if (number > 1) {
      string lower;
      lower = tolower(desc);
      if (lower[0..1] ==      "a ")
	desc = desc[2..-1];
      else if (lower[0..2] == "an ")
	desc = desc[3..-1];
      else if (lower[0..3] == "one ")
	desc = desc[4..-1];
      else if (lower[0..3] == "the ")
	desc = desc[4..-1];
      else if (lower[0..4] == "some ")
	desc = desc[5..-1];
      if (numeric)
	return number + " " + pluralize(desc);
      return convert_int(number) + " " + pluralize(desc);
    }
    return desc;
  }
  return 0;
}


/* return genitive 's'...*/
static string genitive(string s) {
  int i;
  if ((i=strlen(s)) > 0) {
    if (s[i-1] == 's')
      return s+"'";
    else
      return s+"s";
  }
  return s;
}


/* return removed genitive 's'...*/
static string remove_genitive(string s) {
  int i;
  if ((i=strlen(s)-1) >= 0) {
    if (i > 0) {
      if (s[i] == 's' || s[i] == '\'')
	return s[0..i-1];
    }
    else
      return "";
  }
  return s;
}



#undef GRAMMAR_OB
#else /* __AUTO */


/* how to init us */
#define INIT_GRAMMAR() init_grammar()


/* grammar data */
private string *ones;
private string *tens;
private string *ordinal_ones;
private string *ordinal_tens;
private string *ordinal_end;
private mapping vowels;
private mapping noun_exceptions;
private mapping opposite_noun_exceptions;



/*
 * Function name:	convert_int
 * Description:		Returns a string for a number.
 * Arguments:		n - The number.  Numbers from 1000 result in
 *			    "lots of".
 *
 */
varargs string convert_int(int n, int maks) {
  string result;
  if (n < 0) {
    result = "minus ";
    n = -n;
  }
  else
    result = "";
  
  if (n <= 999 && (!maks || n <= maks)) {
    if (n > 99) {
      int i;
      if ((i=n/100) > 1)
	result += convert_int(n/100)+" ";
      result += "hundred";
      n %= 100;
      if (n == 0)
	return result;
      result += " and ";
    }
    if (n >= 20) {
      result += tens[n/10];
      n %= 10;
      if (n == 0)
	return result;
      result += ones[n];
      return result;
    }
    n %= 20;
    result += ones[n];
    return result;
  }
  
  /* "vague" numbers */
  if (n > 2000)
    return result+"thousands of";
  if (n > 200)
    return result+"hundreds of";
  if (n >= 50)
    return result+"lots of";
  if (n >= 10)
    return result+"many";
  if (n >= 5)
    return result+"some";
  if (n >= 2)
    return result+"a few";
  return result+"a very few";
}

mixed convert_string_to_int(string s) {
  string one, ten;
  int n;
  
  s = tolower(s);
  n = member_array(s, ones);
  if (n > -1)
    return n;
  n = member_array(s, tens);
  if (n > -1)
    return n * 10;
  
  if ((sscanf(s, "%s-%s", ten, one) == 2)
      || (sscanf(s, "%s %s", ten, one) == 2)) {
    n = member_array(ten, tens) * 10;
    if (n > -1) {
      n += member_array(one, ones);
      if (n > 20)
        return n;
    }
  }
  return s;
}

/*
 * Function name:	convert_int_to_ordinal
 * Description:		Returns a string for an ordinal number (first, fourth).
 * Arguments:		n - The number.  If a number larger than 999 is given,
 *                      the returned string will hold the number with a .
 *                      appended.
 */
string convert_int_to_ordinal(int n) {
  string result;
  if (n < 0) {
    result = "minus ";
    n = -n;
  }
  else
    result = "";
  
  if (n <= 999) {
    if (n > 99) {
      int i;
      if ((i=n/100) > 1)
	result += convert_int(n/100)+" ";
      result += "hundred";
      n %= 100;
      if (n == 0)
	return result+"th";
      result += " and ";
    }
    if (n >= 20) {
      int i;
      i = n/10;
      n %= 10;
      if (n == 0)
	return result+ordinal_tens[i];
      result += tens[i];
      result += ordinal_ones[n];
      return result;
    }
    n %= 20;
    result += ordinal_ones[n];
    return result;
  }
  return (result == "minus ") ? (string)(-n)+"." : (string)n+".";
}

string convert_int_to_ordinal_number(int n) {
  return (string)n+ordinal_end[abs(n) % 10];
}

mixed convert_ordinal_string_to_int(string s) {
  string one, ten;
  int n;
  
  s = tolower(s);
  n = member_array(s, ordinal_ones);
  if (n > -1)
    return n;
  n = member_array(s, ordinal_tens);
  if (n > -1)
    return n * 10;
  
  if ((sscanf(s, "%s-%s", ten, one) == 2)
      || (sscanf(s, "%s %s", ten, one) == 2)) {
    n = member_array(ten, tens) * 10;
    if (n > -1) {
      n += member_array(one, ordinal_ones);
      if (n > 20)
        return n;
    }
  }
  return s;
}


/*
 * Function name:	pluralize_word
 * Description:		Returns the plural form (hopefully) of a word.
 * Arguments:		str - The word.
 *
 */
string pluralize_word(string str) {
  string s;
  int len;
  s = noun_exceptions[str];
  len = strlen(str);
  
  if (s)
    return s;
  if (str[-3 .. -1] == "ese" ||
      str[-5 .. -1] == "craft")
    return str;			/* No change */
  if (str[-2 .. -1] == "is")
    return str[0 .. -3] + "es";	/* -is -> -es */
  if (str[-2 .. len] == "us") {
    if (len < 4)
      return str + "ses";
    return str[0 .. -3] + "i";	/* -us -> -i */
  }
  if (len == 1)
    return str + "s";
  if (str[-3 .. len] == "man")
    return str[0 .. -4] + "men";	/* -man -> -men */
  switch (str[len - 1]) {
  case 's':
    if (str[len - 2] != 's') {
      if (vowels[str[len - 3]])
	return str + "es";
      return str + "ses";	/* -s -> -ses */
    }
  case 'x':
  case 'h':
    return str + "es";		/* -x -> -xes */
  case 'y':
    switch (str[len - 2]) {
    case 'a':
    case 'e':
    case 'o':
      return str + "s";	/* -[aeo]y -> +s */
    }
    return str[0 .. -2] + "ies";/* -y -> -ies */
  case 'z':
    if (str[len - 2] == 'z')
      return str + "es";	/* -zz -> -zzes */
    return str + "zes";		/* -z -> -zzes */
  }
  return str + "s";
}


/* singularize a word */
string singularize_word(string word) {
  int i;
  if ((i=strlen(word)-1) >= 0) {
    string single;
    if (single = opposite_noun_exceptions[word])
      /* irregular single form */
      return single;

    if (i >= 2) {
      /* word is of 3 characters or more, try to do it regularly */
      if (i > 2 && word[i-2..i] == "ies") {
	/* 'ies' -> 'y' */
	return word[0..i-3]+"y";
      }
      if (word[i] == 's') {
	/* 's' -> '' */
	return word[0..i-1];
      }
    }
  }
  /* if all else fails, return original word. */
  return word;
}



/* add 'a' or 'an' in front of <noun> */
string add_article(string noun) {
  return (vowels[noun[0]] ? "an" : "a") + " " + noun;
}


/* get the article that could be in front of <noun> */
string get_article(string noun) {
  return vowels[noun[0]] ? "an" : "a";
}




/* initialize grammar data in grammar object */
static void init_grammar() {
  ones = ({"zero",
	   "one",
	   "two",
	   "three",
	   "four",
	   "five",
	   "six",
	   "seven",
	   "eight", 
	   "nine",
	   "ten",
	   "eleven",
	   "twelve",
	   "thirteen",
	   "fourteen",
	   "fifteen",
	   "sixteen",
	   "seventeen",
	   "eighteen",
	   "nineteen" });

  tens = ({ "", "ten", "twenty", "thirty", "forty", "fifty",
	    "sixty", "seventy", "eighty", "ninety" });

  ordinal_ones = ({
    "zeroeth",         /* hmm */
    "first",
    "second",
    "third",
    "fourth",
    "fifth",
    "sixth",
    "seventh",
    "eighth",
    "ninth",
    "tenth",
    "eleventh",
    "twelfth",
    "thirteenth",
    "fourteenth",
    "fifteenth",
    "sixteenth",
    "seventeenth",
    "eighteenth",
    "nineteenth" });

  ordinal_tens = ({
    "", "tenth", "twentieth", "thirtieth", "fortieth", "fiftieth",
    "sixtieth", "seventieth", "eightieth", "ninetieth"});

  ordinal_end = ({
    "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
  });

  vowels =
    ([
      'a' : 1,
      'e' : 1,
      'i' : 1,
      'o' : 1,
      'u' : 1,
      'A' : 1,
      'E' : 1,
      'I' : 1,
      'O' : 1,
      'U' : 1,
      ]);
  
  noun_exceptions =
    ([
      "calf"   : 	"calves",
      "dwarf"  :	"dwarves",
      "half"   :	"halves",
      "leaf"   :	"leaves",
      "loaf"   :	"loaves",
      "sheaf"  :	"sheaves",
      "shelf"  :	"shelves",
      "thief"  :	"thieves",
      "wolf"   :	"wolves",
      "scarf"  :	"scarves",
      "wharf"  :	"wharves",
      "knife"  :	"knives",
      "life"   :	"lives",
      "wife"   :	"wives",
      "beans"  :        "beans",
      "child"  :	"children",
      "ox"     :	"oxen",
      "foot"   :	"feet",		/* -oo- -> -ee- */
      "goose"  :	"geese",	/* -oo- -> -ee- */
      "tooth"  :	"teeth",	/* -oo- -> -ee- */
      "person" :	"people",
      "louse"  : 	"lice",
      "mouse"  : 	"mice",		/* -ouse -> -ice */
      "echo"   : 	"echoes",
      "hero"   :	"heroes",
      "potato" :	"potatoes",
      "tomato" :	"tomatoes",
      "negro"  :	"negroes",	/* -o -> -oes */
      "deer"   :	"deer",
      "doe"    :	"doe",
      "fish"   :	"fish",
      "lynx"   :	"lynx",
      "quail"  :	"quail",
      "remains":        "remains",
      "salmon" :	"salmon",
      "sheep"  :	"sheep",
      "swiss"  :	"swiss",
      "trout"  :	"trout",
      "quid"   :	"quid",		/* No change */
      "penny"  :	"pence",	/* Irregular */
      "roman"  :	"romans",	/* Exception from the -man -> -men rule */
      "imago"  :	"imagines",
      ]);

  /* make exceptions for nouns the other way around */
  opposite_noun_exceptions = mkmapping(map_values(noun_exceptions), map_indices(noun_exceptions));
}


#endif /* __AUTO */