/*
* NAME: verb.c
* DESCRIPTION: inherited by all runtime MOO verb objects
*/
# define DEBUG 0
inherit "/std/core";
inherit "/std/data";
inherit "/std/bfuns";
inherit "/std/kfuns";
inherit "/std/string";
# if DEBUG
inherit "/std/vartext";
# else
# define var2str(arg) ""
# endif
# include <objects.h>
# include <moo/data.h>
# include <moo/perms.h>
# include <moo/verb.h>
private int refs; /* references to this object */
private string source; /* tokenized source code for this verb */
int lineno; /* current MOO source line number */
/*
* NAME: set_source()
* DESCRIPTION: receive source code for this verb
*/
void set_source(string code)
{
source = code;
}
/*
* NAME: get_source()
* DESCRIPTION: return the source code for this verb
*/
string *get_source(int full_paren, int indent)
{
return explode(DETOKEN->main(source, full_paren, indent), "\n");
}
/*
* NAME: dump_source()
* DESCRIPTION: return tokenized verb source (debugging)
*/
string dump_source(void)
{
return " " + source;
}
/*
* NAME: ref()
* DESCRIPTION: create a reference to this object
*/
void ref(void)
{
++refs;
}
/*
* NAME: del()
* DESCRIPTION: delete a reference to this object; if last, self-destruct
*/
void del(void)
{
if (! --refs)
destruct_object(this_object());
}
/*
* NAME: get_lineno()
* DESCRIPTION: return the current MOO source line number
*/
int get_lineno(void)
{
return lineno;
}
/*
* NAME: slist()
* DESCRIPTION: construct a spliced list value
*/
static varargs
MOOVAL slist(mixed *info, int *splices, MOOVAL elts...)
{
int i;
for (i = sizeof(splices); i--; )
{
int index;
MOOVAL elt;
elt = elts[index = splices[i]];
if (! LSTP(elt))
return RAISE(E_TYPE);
elts = elts[.. index - 1] + elt + elts[index + 1 ..];
}
return LST(elts);
}
/*
* NAME: table()
* DESCRIPTION: construct a table value (with splices)
*/
static varargs
MOOVAL table(mixed *info, string splices, MOOVAL elts...)
{
mapping table;
int i;
for (table = TNEW(), i = sizeof(elts); i; i -= 2)
{
MOOVAL key, value;
key = elts[i - 2];
if (splices && splices[i / 2 - 1] == '@')
{
if (! TBLP(key))
return RAISE(E_TYPE);
TMERGE(table, TBLVAL(key));
continue;
}
value = elts[i - 1];
TINSERT(table, key, value);
}
return TBL(table);
}
/*
* NAME: ambaggr()
* DESCRIPTION: construct an ambiguous aggregate (list or table, all splices)
*/
static varargs
MOOVAL ambaggr(mixed *info, MOOVAL elts...)
{
mapping table;
MOOVAL *list, elt;
int i, type;
for (table = TNEW(), list = LNEW(), i = sizeof(elts); i--; )
{
elt = elts[i];
if (LSTP(elt))
{
if (type == T_TBL)
return RAISE(E_TYPE);
type = T_LST;
elts = elts[.. i - 1] + elt + elts[i + 1 ..];
}
else if (TBLP(elt))
{
if (type == T_LST)
return RAISE(E_TYPE);
type = T_TBL;
TMERGE(table, TBLVAL(elt));
}
else
return RAISE(E_TYPE);
}
if (type == T_LST)
return LST(elts);
else
return TBL(table);
}
/*
* NAME: buffer()
* DESCRIPTION: construct a buffer value
*/
static varargs
MOOVAL buffer(mixed *info, string splices, MOOVAL elts...)
{
MOOVAL elt;
string buf, c;
int i, sz;
buf = "";
c = "x";
for (i = 0, sz = sizeof(elts); i < sz; ++i)
{
switch (TYPEOF(elt = elts[i]))
{
case T_NUM:
if (splices && splices[i] == '@')
return RAISE(E_TYPE);
c[0] = NUMVAL(elt);
buf += c;
break;
case T_BUF:
if (! splices || splices[i] != '@')
return RAISE(E_TYPE);
buf += BUFVAL(elt);
break;
default:
return RAISE(E_TYPE);
}
}
return BUF(buf);
}
/*
* NAME: do_assignment()
* DESCRIPTION: perform generic indexed assignment
*/
private
MOOVAL do_assignment(mixed *addr, int index, MOOVAL rvalue,
int range_flag, MOOVAL *indices)
{
mixed *handle;
MOOVAL lo, hi, lvalue;
int i, sz, handle_index;
handle = addr;
handle_index = index;
for (i = 0, sz = sizeof(indices) - (range_flag + 1); i < sz; ++i)
{
lo = indices[i];
lvalue = addr[index];
if (TBLP(lvalue))
{
MOOVAL slot;
slot = TLOOKUP(TBLVAL(lvalue), lo);
if (STWP(slot))
return STW(E_RANGE);
lvalue = addr[index] = TBL(TBLVAL(lvalue) + TNEW());
addr = TINSERT(TBLVAL(lvalue), lo, slot);
index = 1;
continue;
}
if (! NUMP(lo) || ! LSTP(lvalue))
return STW(E_TYPE);
if (NUMVAL(lo) < 1 ||
NUMVAL(lo) > sizeof(LSTVAL(lvalue)))
return STW(E_RANGE);
addr = addr[index] = LST(LSTVAL(lvalue) + LNEW());
index = NUMVAL(lo) - 1;
}
lvalue = addr[index];
switch (TYPEOF(lvalue))
{
case T_STR:
if (range_flag)
{
string old;
int b, e;
lo = indices[sz];
hi = indices[sz + 1];
if (! NUMP(lo) || ! NUMP(hi) || ! STRP(rvalue))
return STW(E_TYPE);
b = NUMVAL(lo);
e = NUMVAL(hi);
old = STRVAL(lvalue);
if (e < 0 || b > strlen(old) + 1)
return STW(E_RANGE);
if (--b < 0)
b = 0;
if (e > strlen(old))
e = strlen(old);
addr[index] = STR(old[.. b - 1] + STRVAL(rvalue) + old[e ..]);
return handle[handle_index];
}
else /* ! range */
{
string str;
str = STRVAL(lvalue);
lo = indices[sz];
if (! NUMP(lo) || ! STRP(rvalue))
return STW(E_TYPE);
if (strlen(STRVAL(rvalue)) != 1)
return STW(E_INVARG);
if (NUMVAL(lo) < 1 ||
NUMVAL(lo) > strlen(str))
return STW(E_RANGE);
str[NUMVAL(lo) - 1] = STRVAL(rvalue)[0];
addr[index] = STR(str);
return handle[handle_index];
}
case T_BUF:
if (range_flag)
{
string old;
int b, e;
lo = indices[sz];
hi = indices[sz + 1];
if (! NUMP(lo) || ! NUMP(hi) || ! BUFP(rvalue))
return STW(E_TYPE);
b = NUMVAL(lo);
e = NUMVAL(hi);
old = BUFVAL(lvalue);
if (e < 0 || b > strlen(old) + 1)
return STW(E_RANGE);
if (--b < 0)
b = 0;
if (e > strlen(old))
e = strlen(old);
addr[index] = BUF(old[.. b - 1] + BUFVAL(rvalue) + old[e ..]);
return handle[handle_index];
}
else /* ! range */
{
string buf;
buf = BUFVAL(lvalue);
lo = indices[sz];
if (! NUMP(lo) || ! NUMP(rvalue))
return STW(E_TYPE);
if (NUMVAL(lo) < 1 ||
NUMVAL(lo) > strlen(buf))
return STW(E_RANGE);
buf[NUMVAL(lo) - 1] = NUMVAL(rvalue);
addr[index] = BUF(buf);
return handle[handle_index];
}
case T_LST:
if (range_flag)
{
MOOVAL *old;
int b, e;
lo = indices[sz];
hi = indices[sz + 1];
if (! NUMP(lo) || ! NUMP(hi) || ! LSTP(rvalue))
return STW(E_TYPE);
b = NUMVAL(lo);
e = NUMVAL(hi);
old = LSTVAL(lvalue);
if (e < 0 || b > sizeof(old) + 1)
return STW(E_RANGE);
if (--b < 0)
b = 0;
if (e > sizeof(old))
e = sizeof(old);
--e;
addr[index] = LST(old[0 .. b - 1] +
LSTVAL(rvalue) +
old[e + 1 ..]);
return handle[handle_index];
}
else /* ! range */
{
lo = indices[sz];
if (! NUMP(lo))
return STW(E_TYPE);
if (NUMVAL(lo) < 1 ||
NUMVAL(lo) > sizeof(LSTVAL(lvalue)))
return STW(E_RANGE);
lvalue = LSTVAL(lvalue) + LNEW();
lvalue[NUMVAL(lo) - 1] = rvalue;
addr[index] = LST(lvalue);
return handle[handle_index];
}
case T_TBL:
if (range_flag)
return STW(E_TYPE);
lo = indices[sz];
lvalue = TBLVAL(lvalue) + TNEW();
TINSERT(lvalue, lo, rvalue);
addr[index] = TBL(lvalue);
return handle[handle_index];
default:
return STW(E_TYPE);
}
}
# ifndef INLINE_PUSHVAR
/*
* NAME: var()
* DESCRIPTION: get variable contents
*/
static
MOOVAL var(mixed *info, int var_index)
{
return info[I_VARDEFS][var_index - VAR_OFFSET] == '0' ?
RAISE(E_VARNF) : VARS[var_index];
}
# endif
/*
* NAME: avar()
* DESCRIPTION: perform simple variable assignment
*/
static
MOOVAL avar(mixed *info, int var_index, MOOVAL rvalue)
{
if (var_index >= VAR_OFFSET)
info[I_VARDEFS][var_index - VAR_OFFSET] = '1';
return VARS[var_index] = rvalue;
}
/*
* NAME: avari()
* DESCRIPTION: perform indexed variable assignment
*/
static
MOOVAL avari(mixed *info, int var_index, int range_flag,
MOOVAL *indices, MOOVAL rvalue)
{
MOOVAL ret;
if (var_index >= VAR_OFFSET &&
info[I_VARDEFS][var_index - VAR_OFFSET] == '0')
RAISE(E_VARNF);
ret = do_assignment(VARS, var_index, rvalue, range_flag, indices);
if (STWP(ret))
{
VARS[var_index] = RAISE(STWVAL(ret));
info[I_VARDEFS][var_index - VAR_OFFSET] = '1';
}
return rvalue;
}
/*
* NAME: aprop()
* DESCRIPTION: perform simple property assignment
*/
static
MOOVAL aprop(mixed *info, MOOVAL mobj, MOOVAL pname, MOOVAL rvalue)
{
object ob;
MOOVAL ret;
if (! OBJP(mobj) || ! STRP(pname))
return RAISE(E_TYPE);
if (! (ob = MOOOBJ(OBJVAL(mobj))))
return RAISE(E_INVIND);
ret = ob->set_property(STRVAL(pname), rvalue, info);
return ret != E_NONE ? RAISE(ret) : rvalue;
}
/*
* NAME: apropi()
* DESCRIPTION: perform indexed property assignment
*/
static
MOOVAL apropi(mixed *info, MOOVAL mobj, MOOVAL pname, int range_flag,
MOOVAL *indices, MOOVAL rvalue)
{
MOOVAL current;
object ob;
int ret;
if (! OBJP(mobj) || ! STRP(pname))
return RAISE(E_TYPE);
if (! (ob = MOOOBJ(OBJVAL(mobj))))
return RAISE(E_INVIND);
current = ob->get_property(STRVAL(pname), info);
if (STWP(current))
return RAISE(STWVAL(current));
current = do_assignment( ({ current }), 0, rvalue, range_flag, indices);
if (STWP(current))
current = RAISE(STWVAL(current));
ret = ob->set_property(STRVAL(pname), current, info);
return ret != E_NONE ? RAISE(ret) : rvalue;
}
/*
* NAME: argcheck()
* DESCRIPTION: verify the number of arguments to a builtin function
*/
static
int argcheck(MOOVAL *args, int min, int max)
{
int size;
return (size = sizeof(args)) >= min && (max < 0 || size <= max);
}
/*
* NAME: verbcall()
* DESCRIPTION: call a verb
*/
MOOVAL verbcall(JS_PROTO, MOOVAL mobj, MOOVAL vname, MOOVAL args)
{
MOOVAL pvar;
object ob;
JS_BEGIN;
if (STRP(mobj)) /* call LPC from MOO */
{
string func;
if (! STRP(vname))
return RAISE(E_TYPE);
if (! WIZARDP(info))
return RAISE(E_PERM);
if (catch(ob = load_object(STRVAL(mobj))) || ! ob)
return RAISE(E_INVIND);
if (! function_object(func = STRVAL(vname), ob))
return RAISE(E_VERBNF);
return lpc2moo(call_other(ob, func, moo2lpc(args)...));
}
if (! STRP(vname) || ! OBJP(mobj))
return RAISE(E_TYPE);
if (! (ob = MOOOBJ(OBJVAL(mobj))))
return RAISE(E_INVIND);
pvar = VARS[V_PLAYER];
JS_PREP(1);
RET = ob->call_verb(JS_DATA(1), tolower(STRVAL(vname)),
({ (WIZARDP(info) && OBJP(pvar)) ?
pvar : OBJ(info[I_PLAYER]), /* player */
mobj, /* this */
OBJ(info[I_THIS]), /* caller */
args, /* args */
VARS[V_ARGSTR], /* argstr */
vname, /* verb */
VARS[V_DOBJ], /* dobj */
VARS[V_DOBJSTR], /* dobjstr */
VARS[V_PREPSTR], /* prepstr */
VARS[V_IOBJ], /* iobj */
VARS[V_IOBJSTR], /* iobjstr */
STD_VARS }) );
JS_END;
return STWP(RET) ? RAISE(STWVAL(RET)) : RET;
JS_END;
}
/*
* NAME: prep_fork
* DESCRIPTION: prepare a fork
*/
static
void prep_fork(mixed *stack, mixed *info, int fork_id)
{
int task_id, delay;
mixed *finfo;
finfo = info + ({ });
finfo[I_VARS] += ({ });
finfo[I_TASKID] = task_id = NUMVAL(POP());
finfo[I_DEPTH] = global->get_max_depth();
finfo[I_LINENO] = lineno;
finfo[I_LINK] = 0;
delay = NUMVAL(POP());
global->register_task(finfo, delay, this_object(),
call_out("fork" + (string) fork_id, delay,
stack, finfo));
ref();
}
/*
* NAME: pass()
* DESCRIPTION: call the inherited version of a verb
*/
MOOVAL pass(JS_PROTO, MOOVAL *args)
{
object host, parent;
MOOVAL ret, pvar;
JS_BEGIN;
if (! (host = info[I_HOSTOBJ]) ||
! (parent = host->get_parent()))
return RAISE(E_VERBNF);
pvar = VARS[V_PLAYER];
JS_PREP(1);
RET = parent->call_verb(JS_DATA(1), tolower(info[I_NAME]),
({ (WIZARDP(info) && OBJP(pvar)) ?
pvar : OBJ(info[I_PLAYER]), /* player */
OBJ(info[I_THIS]), /* this */
OBJ(info[I_THIS]), /* caller */
LST(args), /* args */
VARS[V_ARGSTR], /* argstr */
STR(info[I_NAME]), /* verb */
VARS[V_DOBJ], /* dobj */
VARS[V_DOBJSTR], /* dobjstr */
VARS[V_PREPSTR], /* prepstr */
VARS[V_IOBJ], /* iobj */
VARS[V_IOBJSTR], /* iobjstr */
STD_VARS }) );
JS_END;
return STWP(RET) ? RAISE(STWVAL(RET)) : RET;
JS_END;
}
/*
* NAME: abort_task()
* DESCRIPTION: called from the global object to abort a call_out()
*/
void abort_task(int dgd_task_id)
{
remove_call_out(dgd_task_id);
}