/*
* grammar.c
*
* SFUN: grammar sfuns collection
*
* Taken from VikingMUD.
* Modified by Frank Schmidt for DGD.
*
*/
/* don't have variables */
#ifdef __AUTO
/* 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 */