private mixed *ret; private mixed value; private void load_lpc_info(int ix, object ob) { mixed *tmp, *sing; value ret; int il, make_plural = 0; string str; if (!ob) return; if (pluid_list && sizeof(pluid_list) > ix && pluid_list[ix] == 0) { ret = ob->parse_command_plural_id_list(); if (arrayp(ret)) pluid_list[ix] = ret; else { make_plural = 1; pluid_list[ix] = 1; } } if (id_list && sizeof(id_list) > ix && id_list[ix] == 0 && ob) { ret = ob->parse_command_id_list(); if (arrayp(ret)) { id_list[ix] = ret; if (make_plural) pluid_list[ix] = map(ret, (: stringp($1) ? pluralize($1) : 0 :)); } else { id_list[ix] = 1; } } if (adjid_list && sizeof(adjid_list) > ix && adjid_list[ix] == 0 && ob) { ret = ob->parse_command_adjectiv_id_list(); if (arrayp(ret)) adjid_list[ix] = ret; else adjid_list[ix] = 1; } } mixed *parse_command(string cmd, mixed obarr, string pattern) { mixed *saved_ret = ret, *cret; ret = ({ }); /* pattern and command cannot be empty */ if (cmd == "" || pattern = "") return ({ }); if (!stringp(cmd)) error("Bad argument 1 to parse_command().\n"); if (!stringp(pattern)) error("Bad argument 3 to parse_command().\n"); /* array of words in command */ parse_warr = explode(cmd, " "); /* array of pattern elements */ parse_patarr = explode(pattern, " "); #ifndef __NO_ENVIRONMENT__ if (objectp(obarr)) obarr = ({ obarr }) + deep_inventory(obarr); #endif if (!arrayp(obarr)) error("Bad argument 2 to parse_command().\n"); id_list = allocate(sizeof(obarr)); pluid_list = allocate(sizeof(obarr)); adjid_list = allocate(sizeof(obarr)); id_list_d = master()->parse_command_id_list(); pluid_list_d = master()->parse_command_plural_id_list(); adjid_list_d = master()->parse_command_adjectiv_id_list(); prepos_list = master()->parse_command_prepos_list(); allword = master()->parse_command_all_word(); /* * Loop through the pattern. Handle %s but not '/' */ for (six = 0, cix = 0, pix = 0; pix < sizeof(parse_patarr); pix++) { value = 0; fail = 0; if (parse_patarr[pix] == "%s") { /* * We are at end of pattern, scrap up the remaining words and put * them in the fill-in value. */ if (pix == sizeof(parse_patarr - 1)) { store_words_slice(six++, parse_warr, cix, sizeof(parse_warr) - 1); cix = sizeof(parse_warr); } else { /* * There is something after %s, try to parse with the next * pattern. Begin with the current word and step one word for * each fail, until match or end of words. */ ocix = fword = cix; /* current word */ fpix = ++pix; /* pix == next pattern */ do { /* * Parse the following pattern, fill-in values: * stack_args[six] = result of %s stack_args[six + 1] = * result of following pattern, if it is a fill-in * pattern */ fail = sub_parse(obarr, parse_patarr, ref pix, parse_warr, ref cix); if (fail) { cix = ++ocix; pix = fpix; } } while (fail && (cix < sizeof(parse_warr))); /* * We found something mathing the pattern after %s. First * stack_args[six + 1] = result of match Then stack_args[six] * = the skipped words before match */ if (!fail) { if (value) { /* A match with a value fill in param */ store_value(six + 1, value); store_words_slice(six, parse_warr, fword, ocix - 1); six += 2; } else { /* A match with a non value ie 'word' */ store_words_slice(six++, parse_warr, fword, ocix - 1); } value = 0; } } } /* * The pattern was not %s, parse the pattern if it is not '/', a '/' * here is skipped. If match, put in fill-in value. */ else if (parse_patarr[pix] != "/") { fail = sub_parse(obarr, parse_patarr, ref pix, parse_warr, ref cix, ref fail); if (!fail && value) store_value(six++, value); } /* * Terminate parsing if no match */ if (fail) break; } /* * Also fail when there is words left to parse and pattern exhausted */ if (fail || cix < sizeof(parse_warr)) return 0; cret = ret; ret = saved_ret; return cret; } private void store_value(int pos, mixed what) { if (sizeof(ret) <= pos) ret += allocate(ret + 1 - pos); ret[pos] = what; } static void store_words_slice(int pos, mixed *warr, int from, int to) { mixed *slice = warr[from..to]; store_value(pos, implode(slice, " ")); } private static int sub_parse(mixed *obarr, mixed *patarr, int ref pix_in, mixed *warr, int ref cix_in) { int cix, pix; int fail; /* * Fail if we have a pattern left but no words to parse */ if (*cix_in == sizeof(warr)) return 1; cix = *cix_in; pix = *pix_in; fail = one_parse(obarr, patarr[pix], warr, ref cix); while (fail) { pix++; cix = *cix_in; /* * Find the next alternative pattern, consecutive '/' are skipped */ while (pix < sizeof(patarr) && patarr[pix] == "/") { pix++; fail = 0; } if (!fail && pix < sizeof(patarr)) { fail = one_parse(obarr, patarr[pix], warr, ref cix); } else { *pix_in = pix - 1; return 1; } } /* * If there is alternatives left after the mathing pattern, skip them */ if (pix + 1 < sizeof(patarr) && patarr[pix+1] == "/") { while (pix + 1 < sizeof(patarr) && patarr[pix+1] == "/") { pix += 2; } if (pix >= sizeof(patarr)) pix = sizeof(patarr->size); } *cix_in = cix; *pix_in = pix; return fail; } private int one_parse(mixed *obarr, string pat, mixed *warr, int ref cix_in) { int ch, fail; string str1, str2; /* * Fail if we have a pattern left but no words to parse */ if (*cix_in >= sizeof(warr)) return 1; ch = pat[0]; if (ch == '%') ch = pat[1]; switch (ch) { case 'i': case 'I': fail = item_parse(obarr, warr, cix_in); break; #ifndef __NO_ADD_ACTION__ case 'l': case 'L': fail = living_parse(obarr, warr, cix_in); break; #endif case 's': case 'S': value = 0; fail = 0; break; case 'w': case 'W': value = warr[*cix_in]; (*cix_in)++; fail = 0; break; case 'o': case 'O': fail = single_parse(obarr, warr, cix_in); break; case 'p': case 'P': fail = prepos_parse(warr, cix_in); break; case 'd': case 'D': fail = number_parse(obarr, warr, cix_in); break; case '\'': str1 = pat[1..<2]; str2 = warr[*cix_in]; if (pat[<1] == '\'' && str1 == str2) { fail = 0; (*cix_in)++; } else fail = 1; break; case '[': str1 = pat[1..<2]; str2 = warr[*cix_in]; if (str1 == str2) (*cix_in)++; fail = 0; break; default: fail = 0; /* Skip invalid patterns */ } return fail; } string *ord1 = ({"", "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "nineth", "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth" }); string *ord10 = ({"", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"}); string *sord10 = ({"", "", "twentieth", "thirtieth", "fortieth", "fiftieth", "sixtieth", "seventieth", "eightieth", "ninetieth"}); string *num1 = ({"", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"}); string *num10 = ({"", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"}); private int number_parse(mixed *obarr, mixed *warr, int ref cix_in) { int cix, ten, ones, num; string buf; cix = *cix_in; if (sscanf(warr[cix], "%d", num)) { if (num >= 0) { (*cix_in)++; value = num; return 0; } return 1; /* only nonnegative numbers */ } if (warr[cix] == allword) { (*cix_in)++; value = 0; return 0; } /* This next double loop is incredibly stupid. -Beek */ for (ten = 0; ten < 10; ten++) for (ones = 0; ones < 10; ones++) { buf = num10[ten] + (ten > 1 ? num1[ones] : num1[ten * 10 + ones]); if (buf == warr[cix]) { (*cix_in)++; value = ten * 10 + ones; return 0; } } /* this one too */ for (ten = 0; ten < 10; ten++) for (ones = 0; ones < 10; ones++) { buf = (ones ? ord10[ten] : sord10[ten]) + (ten > 1 ? ord1[ones] : ord1[ten*10 + ones]); if (buf == warr[cix]) { (*cix_in)++; value = -(ten * 10 + ones); return 0; } } return 1; } private int item_parse(mixed *obarr, mixed *warr, int ref cix_in) { mixed *tmp, *ret; int cix, tix, obix, plur_flag, max_cix, match_all; tmp = allocate(sizeof(obarr) + 1); if (!number_parse(obarr, warr, cix_in)) { tmp[0] = value; match_all = (value == 0); plur_flag = (match_all || value > 1); have_number = 1; value = 0; } else { plur_flag = 0; match_all = 0; } for (max_cix = *cix_in, tix = 1, obix = 0; obix < sizeof(obarr); obix++) { cix = *cix_in; if (!objectp(obarr[obix])) continue; if (cix == sizeof(warr) && match_all) { tmp[tix++] = obarr[obix]; continue; } load_lpc_info(obix, obarr[obix]); if (match_object(obix, warr, ref cix, ref plur_flag)) { tmp[tix++] = obarr[obix]; max_cix = (max_cix < cix) ? cix : max_cix; } } if (tix < 2) { if (have_number) (*cix_in)--; return 1; } else { if (*cix_in < sizeof(warr)) *cix_in = max_cix + 1; if (!have_number) tmp[0] = !plur_flag; value = tmp[0..tix-1]; return 0; } } #ifndef __NO_ADD_ACTION__ private int living_parse(mixed *obarr, array warr, int ref cix_in, int ref fail) { mixed *live; object ob; int obix, tix; live = allocate(sizeof(obarr)); tix = 0; for (obix = 0; obix < sizeof(obarr); obix++) if (living(obarr[obix])) live[tix++] = obarr[obix]; if (tix && !item_parse(live, warr, cix_in)) return 0; ob = find_player(warr[*cix_in]); if (!ob) ob = find_living(warr[*cix_in]); if (ob) { value = ob; (*cix_in)++; return 0; } return 1; } #endif private int single_parse(mixed *obarr, mixed *warr, int ref cix_in) { int cix, obix, plur_flag; for (obix = 0; obix < sizeof(obarr); obix++) { cix = *cix_in; if (objectp(obarr[obix])) load_lpc_info(obix, obarr[obix]); plur_flag = 0; if (match_object(obix, warr, ref cix, ref plur_flag)) { *cix_in = cix + 1; value = obarr[obix]; return 0; } } return 1; } private int prepos_parse(mixed *warr, int ref cix_in, mixed prepos) { mixed *tarr; string tmp; int pix, tix; if (!prepos || !arrayp(prepos)) prepos = prepos_list; for (pix = 0; pix < sizeof(prepos); pix++) { if (!stringp(prepos[pix])) continue; tmp = prepos[pix]; if (member_array(' ', tmp) == -1) { if (tmp == warr[*cix_in]) { (*cix_in)++; break; } } else { tarr = explode(tmp, " "); if (*cix_in + sizeof(tarr) <= sizeof(warr)) { for (tix = 0; tix < sizeof(tarr); tix++) { if (*cix_in + tix >= sizeof(warr) || warr[*cix_in + tix] != tarr[tix]) break; } if (tix == sizeof(tarr)) { (*cix_in) += sizeof(tarr); break; } } } } if (pix == sizeof(prepos)) { value = 0; return 1; } else { value = prepos[pix]; return 0; } } private int match_object(int obix, mixed *warr, int ref cix_in, int ref plur) { mixed *ids; int il, pos, cplur, old_cix; string str; for (cplur = (*plur * 2); cplur < 4; cplur++) { switch (cplur) { case 0: if (!id_list_d) continue; ids = id_list_d; break; case 1: if (!d_list || sizeof(id_list) <= obix || !arrayp(id_list[obix])) continue; ids = id_list[obix]; break; case 2: if (!pluid_list_d) continue; ids = pluid_list_d; break; case 3: if (!pluid_list || sizeof(gpluid_list) <= obix || !arrayp(gpluid_list[obix])) continue; ids = pluid_list[obix]; break; default: ids = 0; } for (il = 0; il < sizeof(ids); il++) { if (stringp(ids[il])) { str = ids[il]; /* A given id of the object */ old_cix = *cix_in; if ((pos = find_string(str, warr, cix_in)) >= 0) { if (pos == old_cix) { if (cplur > 1) *plur = 1; return 1; } else if (check_adjectiv(obix, warr, old_cix, pos - 1)) { if (cplur > 1) *plur = 1; return 1; } } *cix_in = old_cix; } } } return 0; } static int find_string(string str, mixed *warr, int ref cix_in) { int fpos; string p1; mixed *split; for (; *cix_in < warr->size; (*cix_in)++) { p1 = warr[*cix_in]; if (p1 == str) /* str was one word and we found it */ return *cix_in; if (member_array(' ', str) == -1) continue; /* * If str was multi word we need to make some special checks */ if (*cix_in == sizeof(warr->size) - 1) continue; split = explode(str, " "); /* * warr->size - *cix_in == * 2: One extra word * 3: Two extra words */ if (sizeof(split) > sizeof(warr) - *cix_in) continue; fpos = *cix_in; for (; (*cix_in - fpos) < sizeof(split); (*cix_in)++) { if (split[*cix_in - fpos] == warr[*cix_in]) break; } if ((*cix_in - fpos) == sizeof(split)) return fpos; *cix_in = fpos; } return -1; } private int check_adjectiv(int obix, mixed *warr, int from, int to) { int il, back, fail; string adstr; mixed *ids; if (arrayp(adjid_list[obix])) ids = adjid_list[obix]; else ids = 0; for (fail = 0, il = from; il <= to; il++) { if ((member_array(warr[il], ids) < 0) && (member_array(warr[il], adjid_list_d) < 0)) fail = 1; } /* * Simple case: all adjs were single word */ if (!fail) return 1; if (from == to) return 0; /* * If we now have: "adj1 adj2 adj3 ... adjN" * We must test in order: "adj1 adj2 adj3 .... adjN-1 adjN" "adj1 adj2 adj3 .... adjN-1" "adj1 adj2 adj3 ...." .... * if match for adj1 .. adj3 continue with: * "adj4 adj5 .... adjN-1 adjN" * "adj4 adj5 .... adjN-1" * "adj4 adj5 ...." * ..... */ for (il = from; il <= to;) { /* adj1 .. adjN */ for (back = to; back >= il; back--) { /* back from adjN to adj[il] */ /* * Create teststring with "adj[il] .. adj[back]" */ adstr = ""; for (sum = il; sum <= back; sum++) { /* test "adj[il] .. * adj[back]" */ if (sum > il) adstr += " "; adstr += warr[sum]; } if ((member_array(adstr, ids) < 0) && (member_array(adstr, adjid_list_d) < 0)) continue; else { il = back + 1; /* Match "adj[il] adj[il+1] .. adj[back]" */ back = to; break; } } if (back < to) return 0; } return 1; }