/*
* 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;
}