/* * NAME: data.c * DESCRIPTION: routines for manipulating MOO data */ # define DEBUG 0 inherit "/std/core"; inherit "/std/string"; # if DEBUG inherit "/std/vartext"; # else # define var2str(x) "" # endif # include <objects.h> # include <moo/data.h> # include <moo/errors.h> # include <moo/tokens.h> # include <moo/verb.h> # include <dgd/float.h> private mixed token; /* used for MOO value parsing */ /* * NAME: moo_typeof() * DESCRIPTION: return the type of a MOO value */ static int moo_typeof(MOOVAL arg) { string str; int c; switch (typeof(arg)) { case T_STRING: str = arg; return (strlen(str) && ((c = str[0]) & TE_MAGIC)) ? (c & ~TE_MAGIC) : T_STR; case T_INT: return T_NUM; case T_ARRAY: return T_LST; case T_FLOAT: return T_FLT; case T_MAPPING: return T_TBL; default: error("Unknown MOO datatype"); } } /* * NAME: moo_truthof() * DESCRIPTION: return 1 iff a MOO value is "true" */ static int moo_truthof(MOOVAL arg) { switch (TYPEOF(arg)) { case T_NUM: return (NUMVAL(arg) != 0); case T_STR: return (strlen(STRVAL(arg)) != 0); case T_LST: return (sizeof(LSTVAL(arg)) != 0); case T_FLT: return (FLTVAL(arg) != 0.0); case T_TBL: return (map_sizeof(TBLVAL(arg)) != 0); case T_BUF: return (strlen(BUFVAL(arg)) != 0); default: return 0; } } static int TCOMPARE(mapping table1, mapping table2); /* * NAME: moo_equalp() * DESCRIPTION: return 1 iff two MOO values are equal */ static int moo_equalp(MOOVAL arg1, MOOVAL arg2) { /* lots of representation assumptions */ switch (TYPEOF(arg1)) { case T_NUM: return (NUMP(arg2) && NUMVAL(arg1) == NUMVAL(arg2)) || (FLTP(arg2) && (float) NUMVAL(arg1) == FLTVAL(arg2)); case T_FLT: return (FLTP(arg2) && FLTVAL(arg1) == FLTVAL(arg2)) || (NUMP(arg2) && FLTVAL(arg1) == (float) NUMVAL(arg2)); case T_OBJ: case T_ERR: case T_BUF: return arg1 == arg2; case T_STR: return arg1 == arg2 || (STRP(arg2) && strlen(arg1) == strlen(arg2) && tolower(arg1) == tolower(arg2)); case T_LST: { MOOVAL *list1, *list2; int i; if (! LSTP(arg2)) return 0; list1 = LSTVAL(arg1); list2 = LSTVAL(arg2); if (list1 == list2) /* check to see if they are the same DGD array */ return 1; if ((i = sizeof(list1)) != sizeof(list2)) return 0; while (i--) if (! EQUALP(list1[i], list2[i])) return 0; return 1; } case T_TBL: return TBLP(arg2) && TCOMPARE(TBLVAL(arg1), TBLVAL(arg2)); default: return 0; } } # define HASH(x) (STRP(x) ? tolower(x) : x) /* * NAME: moo_tkeys() * DESCRIPTION: return the keys of a table */ static MOOVAL moo_tkeys(mapping table) { mixed *keys; int i; keys = map_values(table); for (i = sizeof(keys); i--; ) keys[i] = keys[i][0]; return LST(keys); } /* * NAME: moo_tvalues() * DESCRIPTION: return the values of a table */ static MOOVAL moo_tvalues(mapping table) { mixed *values; int i; values = map_values(table); for (i = sizeof(values); i--; ) values[i] = values[i][1]; return LST(values); } /* * NAME: moo_tdelete() * DESCRIPTION: remove a key from a MOO table */ static void moo_tdelete(mapping table, MOOVAL key) { if (LSTP(key) || TBLP(key)) { MOOVAL *keys; int i; keys = map_indices(table); for (i = sizeof(keys); i--; ) if (EQUALP(keys[i], key)) { table[keys[i]] = 0; break; } return; } table[HASH(key)] = 0; } /* * NAME: moo_tinsert() * DESCRIPTION: insert (or replace) a value in a MOO table */ static MOOVAL *moo_tinsert(mapping table, MOOVAL key, MOOVAL value) { if (LSTP(key) || TBLP(key)) TDELETE(table, key); return table[HASH(key)] = ({ key, value }); } /* * NAME: moo_tmerge() * DESCRIPTION: merge one table into anther */ static void moo_tmerge(mapping table1, mapping table2) { MOOVAL *values; int i; values = map_values(table2); for (i = sizeof(values); i--; ) TINSERT(table1, values[i][0], values[i][1]); } /* * NAME: moo_tlookup() * DESCRIPTION: lookup a key in a table; return value or STW(0) */ static MOOVAL moo_tlookup(mapping table, MOOVAL key) { MOOVAL value; if (LSTP(key) || TBLP(key)) { MOOVAL *keys; int i; keys = map_indices(table); for (i = sizeof(keys); i--; ) if (EQUALP(keys[i], key)) return map_values(table)[i][1]; return STW(0); } value = table[HASH(key)]; return value ? value[1] : STW(0); } /* * NAME: moo_tcompare() * DESCRIPTION: return 1 if two MOO tables are equal */ static int moo_tcompare(mapping table1, mapping table2) { MOOVAL *values; int i; if (table1 == table2) /* same DGD mapping? */ return 1; if (map_sizeof(table1) != map_sizeof(table2)) return 0; values = map_values(table1); for (i = sizeof(values); i--; ) if (! EQUALP(TLOOKUP(table2, values[i][0]), values[i][1])) return 0; return 1; } /* * NAME: moo_regexp() * DESCRIPTION: perform regular expression matching (match() / rmatch()) */ static MOOVAL moo_regexp(MOOVAL subject, MOOVAL pattern, int reverse, int case_matters) { string *patbuf; MOOVAL *replacements; int *results, i, j; if (catch(patbuf = global->get_regexp(STRVAL(pattern), ! case_matters))) return STW(E_INVARG); if (! (results = regexp_match(patbuf, STRVAL(subject), reverse))) return LST(LNEW()); replacements = allocate(9); for (i = 2, j = 0; i < 20; i += 2, ++j) { int start, end; start = results[i]; end = results[i + 1]; if (end != -1) ++start, ++end; replacements[j] = LST( ({ NUM(start), NUM(end) }) ); } return LST( ({ NUM(results[0] + 1), NUM(results[1] + 1), LST(replacements), subject }) ); } /* * NAME: moo_escape_str() * DESCRIPTION: insert backslash escapes into a string */ static string moo_escape_str(string str) { int i; for (i = strlen(str); i--; ) { switch (str[i]) { case '\"': case '\\': str = str[.. i - 1] + "\\" + str[i ..]; } } return str; } /* * NAME: moo_unescape_str() * DESCRIPTION: remove backslash escapes, substituting character values */ static string moo_unescape_str(string str) { int i, sz; for (i = 0, sz = strlen(str); i < sz; ++i) { if (str[i] == '\\') { str = str[.. i - 1] + str[i + 1 ..]; --sz; } } return str; } /* * NAME: flt2str() * DESCRIPTION: return a visual string representation for a float (approximate) */ static string flt2str(float flt) { string str; int i, c; str = (string) flt; for (i = strlen(str); i--; ) if ((c = str[i]) == '.' || c == 'e') return str; return str + ".0"; } /* * NAME: flt2internal() * DESCRIPTION: return a precise string representation for a float */ static string flt2internal(float flt) { mixed *split; float mant; int exp, v1, v2; split = frexp(flt); mant = split[0]; exp = split[1]; mant = ldexp(mant, FLT_MANT_DIG + 1); v1 = (int) (mant / 65536.0); v2 = (int) (mant - (float) v1 * 65536.0); return (string) v1 + "/" + (string) v2 + "/" + (string) exp; } /* * NAME: internal2flt() * DESCRIPTION: perform inverse of flt2internal() */ static float internal2flt(string data) { int v1, v2, exp; if (sscanf(data, "%d/%d/%d", v1, v2, exp) != 3) error("Invalid argument"); return ldexp((float) v1 * 65536.0 + (float) v2, exp - FLT_MANT_DIG - 1); } /* * NAME: moo2str() * DESCRIPTION: return a string representation of a MOO value */ static string moo2str(MOOVAL val, int quoteflag) { mixed *elts; string list, buf; int i, sz; switch (TYPEOF(val)) { case T_NUM: return (string) NUMVAL(val); case T_STR: if (quoteflag) return "\"" + moo_escape_str(STRVAL(val)) + "\""; else return STRVAL(val); case T_OBJ: return "#" + OBJVAL(val); case T_ERR: return global->error_name(ERRVAL(val)); case T_LST: elts = LSTVAL(val); if (! (sz = sizeof(elts))) return "{}"; list = "{" + moo2str(elts[0], quoteflag); for (i = 1; i < sz; ++i) list += ", " + moo2str(elts[i], quoteflag); return list + "}"; case T_FLT: return flt2str(FLTVAL(val)); case T_TBL: elts = map_values(TBLVAL(val)); if (! (sz = sizeof(elts))) return "{~}"; list = "{" + moo2str(elts[0][0], quoteflag) + " ~ " + moo2str(elts[0][1], quoteflag); for (i = 1; i < sz; ++i) list += ", " + moo2str(elts[i][0], quoteflag) + " ~ " + moo2str(elts[i][1], quoteflag); return list + "}"; case T_STW: return "\"STW(" + (string) STWVAL(val) + ")\""; case T_BUF: buf = BUFVAL(val); if (! (sz = strlen(buf))) return "[]"; list = "[" + (string) buf[0]; for (i = 1; i < sz; ++i) list += ", " + (string) buf[i]; return list + "]"; case T_IST: return "\"IST()\""; default: error("Bad MOO value"); } } /* * NAME: advance() * DESCRIPTION: fetch the next token */ private void advance(void) { token = LEXAN->advance(); } /* * NAME: match() * DESCRIPTION: expect tokens */ private void match(int what) { string str; if (token == what) advance(); else { str = "Syntax error: `x' expected"; str[15] = what; error(str); } } /* * NAME: parse_value() * DESCRIPTION: return a single MOO value */ private MOOVAL parse_value(void) { MOOVAL value; if (token == TOK_LBRACE) { advance(); if (token == TOK_RBRACE) { advance(); return LST(LNEW()); } else if (token == TOK_ASSOC) { advance(); match(TOK_RBRACE); return TBL(TNEW()); } else { value = parse_value(); if (token == TOK_ASSOC) { mapping table; advance(); TINSERT(table = TNEW(), value, parse_value()); while (token == TOK_COMMA) { MOOVAL key, slot; advance(); key = parse_value(); match(TOK_ASSOC); value = parse_value(); slot = TLOOKUP(table, key); if (STWP(slot)) TINSERT(table, key, value); } match(TOK_RBRACE); return TBL(table); } else { MOOVAL *list; list = ({ value }); while (token == TOK_COMMA) { advance(); LAPPEND(list, parse_value()); } match(TOK_RBRACE); return LST(list); } } } if (token == TOK_LBRACKET) { string buf, c; advance(); if (token == TOK_RBRACKET) { advance(); return BUF(""); } value = parse_value(); if (! NUMP(value)) error("Number expected as buffer element"); buf = c = "x"; buf[0] = NUMVAL(value); while (token == TOK_COMMA) { advance(); value = parse_value(); if (! NUMP(value)) error("Number expected as buffer element"); c[0] = NUMVAL(value); buf += c; } match(TOK_RBRACKET); return BUF(buf); } if (token == TOK_MINUS) { /* next token must be a literal number or float */ advance(); if (! arrayp(token)) error("Number expected after unary minus"); if (token[0] == TOK_LIT_NUM) value = NUM(-token[1]); else if (token[0] == TOK_LIT_FLT) value = FLT(-((float) token[1])); else error("Number expected after unary minus"); advance(); return value; } if (! arrayp(token)) error("Syntax error"); switch (token[0]) { case TOK_LIT_NUM: value = NUM(token[1]); break; case TOK_LIT_STR: value = STR(moo_unescape_str(token[1])); break; case TOK_LIT_OBJ: value = OBJ(token[1]); break; case TOK_LIT_ERR: value = ERR(token[1]); break; case TOK_LIT_FLT: value = FLT((float) token[1]); break; default: error("Nonliteral in expression"); } advance(); return value; } /* * NAME: str2moo() * DESCRIPTION: parse a string as a MOO literal value and return it */ static MOOVAL str2moo(string str) { MOOVAL value; string msg; /* This routine is unusual because it uses the lexical analyzer as an independent object, rather than inheriting from it. This is done to save memory used by the analyzer's tables. */ LEXAN->set_input(str); if ((msg = catch(advance(), value = parse_value())) || ((msg = "Syntax error"), token != TOK_EOF)) return LST( ({ NUM(0), STR(msg) }) ); else return LST( ({ NUM(1), value }) ); } /* * NAME: objlist2moo() * DESCRIPTION: convert a list of objects into a list of MOOVAL references */ static MOOVAL *objlist2moo(object *list) { MOOVAL *refs; int i; for (refs = allocate(i = sizeof(list)); i--; refs[i] = OBJ_OBJNUM(list[i])); return LST(refs); } /* * NAME: strlist2moo() * DESCRIPTION: convert a list of strings into MOOVAL references */ static MOOVAL *strlist2moo(string *list) { MOOVAL *refs; int i; for (refs = allocate(i = sizeof(list)); i--; refs[i] = STR(list[i])); return LST(refs); } /* * NAME: verb_vars() * DESCRIPTION: return a standard verb variable array */ static varargs MOOVAL *verb_vars(mixed *info, object obj, string verb, MOOVAL args...) { MOOVAL pvar; pvar = VARS[V_PLAYER]; return ({ (WIZARDP(info) && OBJP(pvar)) ? pvar : OBJ(info[I_PLAYER]), /* player */ OBJ_OBJNUM(obj), /* this */ OBJ(info[I_THIS]), /* caller */ LST(args), /* args */ STR(""), /* argstr */ STR(verb), /* verb */ OBJ(-1), /* dobj */ STR(""), /* dobjstr */ STR(""), /* prepstr */ OBJ(-1), /* iobj */ STR(""), /* iobjstr */ STD_VARS }); } /* * NAME: server_vars() * DESCRIPTION: return a standard server task verb variable array */ static varargs MOOVAL *server_vars(int player, object this, string verb, string argstr, MOOVAL args...) { return ({ OBJ(player), /* player */ OBJ_OBJNUM(this), /* this */ OBJ(player), /* caller */ LST(args), /* args */ argstr ? STR(argstr) : STR(""), /* argstr */ STR(verb), /* verb */ OBJ(-1), /* dobj */ STR(""), /* dobjstr */ STR(""), /* prepstr */ OBJ(-1), /* iobj */ STR(""), /* iobjstr */ STD_VARS }); } /* * NAME: moo2lpc() * DESCRIPTION: translate a MOO value into an LPC value */ static mixed moo2lpc(MOOVAL arg) { switch (TYPEOF(arg)) { case T_NUM: return NUMVAL(arg); case T_STR: return STRVAL(arg); case T_OBJ: return MOOOBJ(OBJVAL(arg)); case T_ERR: return global->error_name(ERRVAL(arg)); case T_LST: { mixed *list; MOOVAL *moo; int i; moo = LSTVAL(arg); list = allocate(i = sizeof(moo)); while (i--) list[i] = moo2lpc(moo[i]); return list; } case T_FLT: return FLTVAL(arg); case T_TBL: { mapping map; MOOVAL *keys, *values; int i; keys = TKEYS(TBLVAL(arg)); values = TVALUES(TBLVAL(arg)); map = ([ ]); for (i = sizeof(keys); i--; ) map[moo2lpc(keys[i])] = moo2lpc(values[i]); return map; } case T_BUF: return BUFVAL(arg); default: return 0; } } /* * NAME: lpc2moo() * DESCRIPTION: translate an LPC value into a MOO value */ MOOVAL lpc2moo(mixed arg) { switch (typeof(arg)) { case T_INT: return NUM(arg); case T_FLOAT: return FLT(arg); case T_STRING: { int i, c; string str; str = arg; for (i = strlen(str); i--; ) if (((c = str[i]) < ' ' && c != '\t') || c > '~') return BUF(str); return STR(str); } case T_OBJECT: { string name; int id; name = object_name(arg); return sscanf(name, "/moo/%d", id) ? OBJ(id) : STR(name); } case T_ARRAY: { mixed *list; MOOVAL *moo; int i; list = arg; moo = allocate(i = sizeof(list)); while (i--) moo[i] = lpc2moo(list[i]); return LST(moo); } case T_MAPPING: { MOOVAL key; mixed *keys, *vals; mapping map; int i; keys = map_indices(arg); vals = map_values(arg); for (map = TNEW(), i = sizeof(keys); i--; ) TINSERT(map, lpc2moo(keys[i]), lpc2moo(vals[i])); return TBL(map); } default: return NUM(0); } } /* * NAME: verbname_match() * DESCRIPTION: return 1 iff the given string matches the given verb name */ static int verbname_match(string verb, string word) { string map; int i, j, szv, szw, star, c; map = "\000\001\002\003\004\005\006\007" + "\010\011\012\013\014\015\016\017" + "\020\021\022\023\024\025\026\027" + "\030\031\032\033\034\035\036\037" + "\040\041\042\043\044\045\046\047" + "\050\051\052\053\054\055\056\057" + "\060\061\062\063\064\065\066\067" + "\070\071\072\073\074\075\076\077" + "\100\141\142\143\144\145\146\147" + "\150\151\152\153\154\155\156\157" + "\160\161\162\163\164\165\166\167" + "\170\171\172\133\134\135\136\137" + "\140\141\142\143\144\145\146\147" + "\150\151\152\153\154\155\156\157" + "\160\161\162\163\164\165\166\167" + "\170\171\172\173\174\175\176\177" + "\200\201\202\203\204\205\206\207" + "\210\211\212\213\214\215\216\217" + "\220\221\222\223\224\225\226\227" + "\230\231\232\233\234\235\236\237" + "\240\241\242\243\244\245\246\247" + "\250\251\252\253\254\255\256\257" + "\260\261\262\263\264\265\266\267" + "\270\271\272\273\274\275\276\277" + "\300\301\302\303\304\305\306\307" + "\310\311\312\313\314\315\316\317" + "\320\321\322\323\324\325\326\327" + "\330\331\332\333\334\335\336\337" + "\340\341\342\343\344\345\346\347" + "\350\351\352\353\354\355\356\357" + "\360\361\362\363\364\365\366\367" + "\370\371\372\373\374\375\376\377"; szv = strlen(verb); szw = strlen(word); while (i < szv) { j = 0; star = 0; while (1) { while (i < szv && verb[i] == '*') { ++i; star = (i == szv || verb[i] == ' ') ? 1 : -1; } if (i == szv || (c = verb[i]) == ' ' || j == szw || map[word[j]] != map[c]) break; ++j, ++i; } if (j == szw ? (star != 0 || i == szv || verb[i] == ' ') : (star == 1)) return 1; while (i < szv && verb[i] != ' ') ++i; while (i < szv && verb[i] == ' ') ++i; } return 0; } /* * NAME: moo_ext2int() * DESCRIPTION: convert value to internal storage format */ static mixed moo_ext2int(MOOVAL value) { string store; mixed *list; if (! LSTP(value)) return value; if (sizeof(list = LSTVAL(value)) == 0) return IST(""); else if (catch(store = IST("\n" + implode(list, "\n") + "\n"))) { int i; for (i = sizeof(list += LNEW()); i--; ) list[i] = moo_ext2int(list[i]); return LST(list); } else return store; } /* * NAME: moo_int2ext() * DESCRIPTION: convert internal storage format to external value */ static MOOVAL moo_int2ext(mixed store) { if (LSTP(store)) { mixed *list; int i; for (i = sizeof(list = LSTVAL(store) + LNEW()); i--; ) list[i] = moo_int2ext(list[i]); return LST(list); } else if (ISTP(store)) return explode(ISTVAL(store), "\n"); else return store; }