/* Primitives package */ #include "copyright.h" #include "config.h" #include <stdio.h> #include <ctype.h> #include "db.h" #include "inst.h" #include "externs.h" #include "match.h" #include "interface.h" #include "params.h" #include "tune.h" #include "fbstrings.h" #include "interp.h" static struct inst *oper1, *oper2, *oper3, *oper4; static struct inst temp1, temp2, temp3; static int result, tmp; static dbref ref; static char buf[BUFFER_LEN]; void prim_pop(PRIM_PROTOTYPE) { CHECKOP(1); oper1 = POP(); CLEAR(oper1); } void prim_dup(PRIM_PROTOTYPE) { CHECKOP_READONLY(1); nargs = 0; CHECKOFLOW(1); copyinst(&arg[*top - 1], &arg[*top]); (*top)++; } void prim_popn(PRIM_PROTOTYPE) { int i, count; CHECKOP(1); oper1 = POP(); if (oper1->type != PROG_INTEGER) abort_interp("Operand not an integer."); count = oper1->data.number; CLEAR(oper1); if (count < 0) abort_interp("Operand is negative."); for (i = count; i > 0; i--) { CHECKOP(1); oper1 = POP(); CLEAR(oper1); } } void prim_dupn(PRIM_PROTOTYPE) { int i; CHECKOP(1); oper1 = POP(); if (oper1->type != PROG_INTEGER) abort_interp("Operand is not an integer."); result = oper1->data.number; if (result < 0) abort_interp("Operand is negative."); CLEAR(oper1); CHECKOP(result); nargs = 0; CHECKOFLOW(result); for (i = result; i > 0; i--) { copyinst(&arg[*top - result], &arg[*top]); (*top)++; } } void prim_ldup(PRIM_PROTOTYPE) { int i; CHECKOP_READONLY(1); nargs = 0; if (arg[*top - 1].type != PROG_INTEGER) abort_interp("Operand is not an integer."); result = arg[*top - 1].data.number; if (result < 0) abort_interp("Operand is negative."); result++; CHECKOP_READONLY(result); nargs = 0; CHECKOFLOW(result); for (i = result; i > 0; i--) { copyinst(&arg[*top - result], &arg[*top]); (*top)++; } } void prim_at(PRIM_PROTOTYPE) { CHECKOP(1); temp1 = *(oper1 = POP()); if ((temp1.type != PROG_VAR) && (temp1.type != PROG_LVAR) && (temp1.type != PROG_SVAR)) abort_interp("Non-variable argument."); if (temp1.data.number >= MAX_VAR || temp1.data.number < 0) abort_interp("Variable number out of range."); if (temp1.type == PROG_LVAR) { /* LOCALVAR */ struct localvars *tmp = localvars_get(fr, program); copyinst(&(tmp->lvars[temp1.data.number]), &arg[(*top)++]); } else if (temp1.type == PROG_VAR) { /* GLOBALVAR */ copyinst(&(fr->variables[temp1.data.number]), &arg[(*top)++]); } else { /* SCOPEDVAR */ struct inst *tmp; tmp = scopedvar_get(fr, 0, temp1.data.number); if (!tmp) abort_interp("Scoped variable number out of range."); copyinst(tmp, &arg[(*top)++]); } CLEAR(&temp1); } void prim_bang(PRIM_PROTOTYPE) { CHECKOP(2); oper1 = POP(); oper2 = POP(); if ((oper1->type != PROG_VAR) && (oper1->type != PROG_LVAR) && (oper1->type != PROG_SVAR)) abort_interp("Non-variable argument (2)"); if (oper1->data.number >= MAX_VAR || oper1->data.number < 0) abort_interp("Variable number out of range. (2)"); if (oper1->type == PROG_LVAR) { /* LOCALVAR */ struct localvars *tmp = localvars_get(fr, program); CLEAR(&(tmp->lvars[oper1->data.number])); copyinst(oper2, &(tmp->lvars[oper1->data.number])); } else if (oper1->type == PROG_VAR) { /* GLOBALVAR */ CLEAR(&(fr->variables[oper1->data.number])); copyinst(oper2, &(fr->variables[oper1->data.number])); } else { /* SCOPEDVAR */ struct inst *tmp; tmp = scopedvar_get(fr, 0, oper1->data.number); if (!tmp) abort_interp("Scoped variable number out of range."); CLEAR(tmp); copyinst(oper2, tmp); } CLEAR(oper1); CLEAR(oper2); } void prim_var(PRIM_PROTOTYPE) { CHECKOP(1); oper1 = POP(); if (oper1->type != PROG_INTEGER) abort_interp("Non-integer argument."); result = oper1->data.number; CLEAR(oper1); push(arg, top, PROG_VAR, MIPSCAST & result); } void prim_localvar(PRIM_PROTOTYPE) { CHECKOP(1); oper1 = POP(); if (oper1->type != PROG_INTEGER) abort_interp("Non-integer argument."); result = oper1->data.number; CLEAR(oper1); push(arg, top, PROG_LVAR, MIPSCAST & result); } void prim_swap(PRIM_PROTOTYPE) { CHECKOP(2); oper1 = POP(); temp2 = *(oper2 = POP()); arg[(*top)++] = *oper1; arg[(*top)++] = temp2; /* don't clean! */ } void prim_over(PRIM_PROTOTYPE) { CHECKOP_READONLY(2); CHECKOFLOW(1); copyinst(&arg[*top - 2], &arg[*top]); (*top)++; } void prim_pick(PRIM_PROTOTYPE) { CHECKOP(1); temp1 = *(oper1 = POP()); if (temp1.type != PROG_INTEGER || temp1.data.number <= 0) abort_interp("Operand not a positive integer."); CHECKOP_READONLY(temp1.data.number); copyinst(&arg[*top - temp1.data.number], &arg[*top]); (*top)++; } void prim_put(PRIM_PROTOTYPE) { CHECKOP(2); oper1 = POP(); oper2 = POP(); if (oper1->type != PROG_INTEGER || oper1->data.number <= 0) abort_interp("Operand not a positive integer."); tmp = oper1->data.number; CHECKOP(tmp); CLEAR(&arg[*top - tmp]); copyinst(oper2, &arg[*top - tmp]); CLEAR(oper1); CLEAR(oper2); } void prim_rot(PRIM_PROTOTYPE) { CHECKOP(3); oper1 = POP(); oper2 = POP(); temp3 = *(oper3 = POP()); arg[(*top)++] = *oper2; arg[(*top)++] = *oper1; arg[(*top)++] = temp3; } void prim_rotate(PRIM_PROTOTYPE) { CHECKOP(1); oper1 = POP(); if (oper1->type != PROG_INTEGER) abort_interp("Invalid argument type."); tmp = oper1->data.number; /* Depth on stack */ CHECKOP(abs(tmp)); if (tmp > 0) { temp2 = arg[*top - tmp]; for (; tmp > 0; tmp--) arg[*top - tmp] = arg[*top - tmp + 1]; arg[*top - 1] = temp2; } else if (tmp < 0) { temp2 = arg[*top - 1]; for (tmp = -1; tmp > oper1->data.number; tmp--) arg[*top + tmp] = arg[*top + tmp - 1]; arg[*top + tmp] = temp2; } CLEAR(oper1); } void prim_dbtop(PRIM_PROTOTYPE) { CHECKOP(0); ref = (dbref) db_top; CHECKOFLOW(1); PushObject(ref); } void prim_depth(PRIM_PROTOTYPE) { CHECKOP(0); result = *top; CHECKOFLOW(1); PushInt(result); } void prim_version(PRIM_PROTOTYPE) { CHECKOP(0); CHECKOFLOW(1); PushString(VERSION); } void prim_prog(PRIM_PROTOTYPE) { CHECKOP(0); ref = (dbref) program; CHECKOFLOW(1); PushObject(ref); } void prim_trig(PRIM_PROTOTYPE) { CHECKOP(0); ref = (dbref) fr->trig; CHECKOFLOW(1); PushObject(ref); } void prim_caller(PRIM_PROTOTYPE) { CHECKOP(0); ref = (dbref) fr->caller.st[fr->caller.top - 1]; CHECKOFLOW(1); PushObject(ref); } void prim_intp(PRIM_PROTOTYPE) { CHECKOP(1); oper1 = POP(); result = (oper1->type == PROG_INTEGER); CLEAR(oper1); PushInt(result); } void prim_floatp(PRIM_PROTOTYPE) { CHECKOP(1); oper1 = POP(); result = (oper1->type == PROG_FLOAT); CLEAR(oper1); PushInt(result); } void prim_arrayp(PRIM_PROTOTYPE) { CHECKOP(1); oper1 = POP(); result = (oper1->type == PROG_ARRAY); CLEAR(oper1); PushInt(result); } void prim_dictionaryp(PRIM_PROTOTYPE) { CHECKOP(1); oper1 = POP(); result = (oper1->type == PROG_ARRAY && oper1->data.array && oper1->data.array->type == ARRAY_DICTIONARY); CLEAR(oper1); PushInt(result); } void prim_stringp(PRIM_PROTOTYPE) { CHECKOP(1); oper1 = POP(); result = (oper1->type == PROG_STRING); CLEAR(oper1); PushInt(result); } void prim_dbrefp(PRIM_PROTOTYPE) { CHECKOP(1); oper1 = POP(); result = (oper1->type == PROG_OBJECT); CLEAR(oper1); PushInt(result); } void prim_addressp(PRIM_PROTOTYPE) { CHECKOP(1); oper1 = POP(); result = (oper1->type == PROG_ADD); CLEAR(oper1); PushInt(result); } void prim_lockp(PRIM_PROTOTYPE) { CHECKOP(1); oper1 = POP(); result = (oper1->type == PROG_LOCK); CLEAR(oper1); PushInt(result); } #define ABORT_CHECKARGS(msg) { if (*top == stackpos+1) snprintf(zbuf, sizeof(zbuf), "%s (top)", msg); else snprintf(zbuf, sizeof(zbuf), "%s (top-%d)", msg, ((*top)-stackpos-1)); abort_interp(zbuf); } #define MaxComplexity 18 /* A truly ridiculously high number! */ void prim_checkargs(PRIM_PROTOTYPE) { int currpos, stackpos; int rngstktop = 0; enum { itsarange, itsarepeat } rngstktyp[MaxComplexity]; int rngstkpos[MaxComplexity]; int rngstkcnt[MaxComplexity]; char zbuf[BUFFER_LEN]; CHECKOP(1); oper1 = POP(); /* string argument */ if (oper1->type != PROG_STRING) abort_interp("Non string argument."); if (!oper1->data.string) { /* if null string, then no args expected. */ CLEAR(oper1); return; } strcpy(buf, oper1->data.string->data); /* copy into local buffer */ currpos = strlen(buf) - 1; stackpos = *top - 1; while (currpos >= 0) { if (isdigit(buf[currpos])) { if (rngstktop >= MaxComplexity) abort_interp("Argument expression ridiculously complex."); tmp = 1; result = 0; while ((currpos >= 0) && isdigit(buf[currpos])) { result = result + (tmp * (buf[currpos] - '0')); tmp = tmp * 10; currpos--; } if (result == 0) abort_interp("Bad multiplier '0' in argument expression."); if (result >= STACK_SIZE) abort_interp("Multiplier too large in argument expression."); rngstktyp[rngstktop] = itsarepeat; rngstkcnt[rngstktop] = result; rngstkpos[rngstktop] = currpos; rngstktop++; } else if (buf[currpos] == '}') { if (rngstktop >= MaxComplexity) abort_interp("Argument expression ridiculously complex."); if (stackpos < 0) ABORT_CHECKARGS("Stack underflow."); if (arg[stackpos].type != PROG_INTEGER) ABORT_CHECKARGS("Expected an integer range counter."); result = arg[stackpos].data.number; if (result < 0) ABORT_CHECKARGS("Range counter should be non-negative."); rngstkpos[rngstktop] = currpos - 1; rngstkcnt[rngstktop] = result; rngstktyp[rngstktop] = itsarange; rngstktop++; currpos--; if (result == 0) { while ((currpos > 0) && (buf[currpos] != '{')) currpos--; } stackpos--; } else if (buf[currpos] == '{') { if (rngstktop <= 0) abort_interp("Mismatched { in argument expression"); if (rngstktyp[rngstktop - 1] != itsarange) abort_interp("Misformed argument expression."); if (--rngstkcnt[rngstktop - 1] > 0) { currpos = rngstkpos[rngstktop - 1]; } else { rngstktop--; currpos--; if (rngstktop && (rngstktyp[rngstktop - 1] == itsarepeat)) { if (--rngstkcnt[rngstktop - 1] > 0) { currpos = rngstkpos[rngstktop - 1]; } else { rngstktop--; } } } } else { switch (buf[currpos]) { case 'i': if (stackpos < 0) ABORT_CHECKARGS("Stack underflow."); if (arg[stackpos].type != PROG_INTEGER) ABORT_CHECKARGS("Expected an integer."); break; case 'n': if (stackpos < 0) ABORT_CHECKARGS("Stack underflow."); if (arg[stackpos].type != PROG_FLOAT) ABORT_CHECKARGS("Expected a float."); break; case 's': case 'S': if (stackpos < 0) ABORT_CHECKARGS("Stack underflow."); if (arg[stackpos].type != PROG_STRING) ABORT_CHECKARGS("Expected a string."); if (buf[currpos] == 'S' && !arg[stackpos].data.string) ABORT_CHECKARGS("Expected a non-null string."); break; case 'd': case 'p': case 'r': case 't': case 'e': case 'f': case 'D': case 'P': case 'R': case 'T': case 'E': case 'F': if (stackpos < 0) ABORT_CHECKARGS("Stack underflow."); if (arg[stackpos].type != PROG_OBJECT) ABORT_CHECKARGS("Expected a dbref."); ref = arg[stackpos].data.objref; if ((ref >= db_top) || (ref < HOME)) ABORT_CHECKARGS("Invalid dbref."); switch (buf[currpos]) { case 'D': if ((ref < 0) && (ref != HOME)) ABORT_CHECKARGS("Invalid dbref."); if (Typeof(ref) == TYPE_GARBAGE) ABORT_CHECKARGS("Invalid dbref."); case 'd': if (ref < HOME) ABORT_CHECKARGS("Invalid dbref."); break; case 'P': if (ref < 0) ABORT_CHECKARGS("Expected player dbref."); case 'p': if ((ref >= 0) && (Typeof(ref) != TYPE_PLAYER)) ABORT_CHECKARGS("Expected player dbref."); if (ref == HOME) ABORT_CHECKARGS("Expected player dbref."); break; case 'R': if ((ref < 0) && (ref != HOME)) ABORT_CHECKARGS("Expected room dbref."); case 'r': if ((ref >= 0) && (Typeof(ref) != TYPE_ROOM)) ABORT_CHECKARGS("Expected room dbref."); break; case 'T': if (ref < 0) ABORT_CHECKARGS("Expected thing dbref."); case 't': if ((ref >= 0) && (Typeof(ref) != TYPE_THING)) ABORT_CHECKARGS("Expected thing dbref."); if (ref == HOME) ABORT_CHECKARGS("Expected player dbref."); break; case 'E': if (ref < 0) ABORT_CHECKARGS("Expected exit dbref."); case 'e': if ((ref >= 0) && (Typeof(ref) != TYPE_EXIT)) ABORT_CHECKARGS("Expected exit dbref."); if (ref == HOME) ABORT_CHECKARGS("Expected player dbref."); break; case 'F': if (ref < 0) ABORT_CHECKARGS("Expected program dbref."); case 'f': if ((ref >= 0) && (Typeof(ref) != TYPE_PROGRAM)) ABORT_CHECKARGS("Expected program dbref."); if (ref == HOME) ABORT_CHECKARGS("Expected player dbref."); break; } break; case '?': if (stackpos < 0) ABORT_CHECKARGS("Stack underflow."); break; case 'l': if (stackpos < 0) ABORT_CHECKARGS("Stack underflow."); if (arg[stackpos].type != PROG_LOCK) ABORT_CHECKARGS("Expected a lock boolean expression."); break; case 'v': if (stackpos < 0) ABORT_CHECKARGS("Stack underflow."); if (arg[stackpos].type != PROG_VAR && arg[stackpos].type != PROG_LVAR && arg[stackpos].type != PROG_SVAR ) { ABORT_CHECKARGS("Expected a variable."); } break; case 'a': if (stackpos < 0) ABORT_CHECKARGS("Stack underflow."); if (arg[stackpos].type != PROG_ADD) ABORT_CHECKARGS("Expected a function address."); break; case ' ': /* this is meaningless space. Ignore it. */ stackpos++; break; default: abort_interp("Unkown argument type in expression."); break; } currpos--; /* decrement string index */ stackpos--; /* move on to next stack item down */ /* are we expecting a repeat of the last argument or range? */ if ((rngstktop > 0) && (rngstktyp[rngstktop - 1] == itsarepeat)) { /* is the repeat is done yet? */ if (--rngstkcnt[rngstktop - 1] > 0) { /* no, repeat last argument or range */ currpos = rngstkpos[rngstktop - 1]; } else { /* yes, we're done with this repeat */ rngstktop--; } } } } /* while loop */ if (rngstktop > 0) abort_interp("Badly formed argument expression."); /* Oops. still haven't finished a range or repeat expression. */ CLEAR(oper1); /* clear link to shared string */ } #undef ABORT_CHECKARGS void prim_mode(PRIM_PROTOTYPE) { CHECKOP(0); result = fr->multitask; CHECKOFLOW(1); PushInt(result); } void prim_mark(PRIM_PROTOTYPE) { CHECKOP(0); CHECKOFLOW(1); PushMark(); } void prim_findmark(PRIM_PROTOTYPE) { int depth, height, count; CHECKOP(0); depth = 1; height = *top - 1; while (height >= 0 && arg[height].type != PROG_MARK) { height--; depth++; } count = depth - 1; if (height < 0) abort_interp("No matching mark on stack!"); if (depth > 1) { temp2 = arg[*top - depth]; for (; depth > 1; depth--) arg[*top - depth] = arg[*top - depth + 1]; arg[*top - 1] = temp2; } oper1 = POP(); CLEAR(oper1); PushInt(count); } void prim_setmode(PRIM_PROTOTYPE) { CHECKOP(1); oper1 = POP(); if (oper1->type != PROG_INTEGER) abort_interp("Invalid argument type."); result = oper1->data.number; switch (result) { case BACKGROUND: fr->been_background = 1; fr->writeonly = 1; break; case FOREGROUND: if (fr->been_background) abort_interp("Cannot FOREGROUND a BACKGROUNDed program."); break; case PREEMPT: break; default: abort_interp("Invalid mode."); break; } fr->multitask = result; CLEAR(oper1); } void prim_interp(PRIM_PROTOTYPE) { struct inst *oper1 = NULL; /* prevents re-entrancy issues! */ struct inst *oper2 = NULL; /* prevents re-entrancy issues! */ struct inst *oper3 = NULL; /* prevents re-entrancy issues! */ struct inst *rv=NULL; char buf[BUFFER_LEN]; struct frame *tmpfr; CHECKOP(3); oper3 = POP(); /* string -- top stack argument */ oper2 = POP(); /* dbref -- trigger */ oper1 = POP(); /* dbref -- Program to run */ if (!valid_object(oper1) || Typeof(oper1->data.objref) != TYPE_PROGRAM) abort_interp("Bad program reference. (1)"); if (!valid_object(oper2)) abort_interp("Bad object. (2)"); if ((mlev < 3) && !permissions(ProgUID, oper2->data.objref)) abort_interp("Permission denied."); if (fr->level > 8) abort_interp("Interp call loops not allowed."); CHECKREMOTE(oper2->data.objref); strcpy(buf, match_args); strcpy(match_args, oper3->data.string ? oper3->data.string->data : ""); tmpfr = interp(fr->descr, player, DBFETCH(player)->location, oper1->data.objref, oper2->data.objref, PREEMPT, STD_HARDUID, 0); if (tmpfr) { rv = interp_loop(player, oper1->data.objref, tmpfr, 1); } strcpy(match_args, buf); CLEAR(oper3); CLEAR(oper2); CLEAR(oper1); if (rv) { if (rv->type < PROG_STRING) { push(arg, top, rv->type, MIPSCAST(&rv->data.number)); } else { push(arg, top, rv->type, MIPSCAST(rv->data.string)); } } else { PushNullStr; } } /* Internal stack primitives */ struct forvars *push_for(struct forvars *); struct forvars *pop_for(struct forvars *); void prim_for(PRIM_PROTOTYPE) { int fortop; CHECKOP(3); oper3 = POP(); /* step */ oper2 = POP(); /* end */ oper1 = POP(); /* start */ if (oper1->type != PROG_INTEGER) abort_interp("Starting count expected. (1)"); if (oper2->type != PROG_INTEGER) abort_interp("Ending count expected. (2)"); if (oper3->type != PROG_INTEGER) abort_interp("Step count expected. (3)"); if (fr->fors.top >= STACK_SIZE) abort_interp("Too many nested FOR loops."); fortop = fr->fors.top++; fr->fors.st = push_for(fr->fors.st); copyinst(oper1, &fr->fors.st->cur); copyinst(oper2, &fr->fors.st->end); fr->fors.st->step = oper3->data.number; fr->fors.st->didfirst = 0; if (fr->trys.st) fr->trys.st->for_count++; CLEAR(oper1); CLEAR(oper2); CLEAR(oper3); } void prim_foreach(PRIM_PROTOTYPE) { int fortop; CHECKOP(1); oper1 = POP(); if (oper1->type != PROG_ARRAY) abort_interp("Array argument expected. (1)"); if (fr->fors.top >= STACK_SIZE) abort_interp("Too many nested FOR loops."); fortop = fr->fors.top++; fr->fors.st = push_for(fr->fors.st); fr->fors.st->cur.type = PROG_INTEGER; fr->fors.st->cur.line = 0; fr->fors.st->cur.data.number = 0; if (fr->trys.st) fr->trys.st->for_count++; copyinst(oper1, &fr->fors.st->end); fr->fors.st->step = 0; fr->fors.st->didfirst = 0; CLEAR(oper1); } void prim_foriter(PRIM_PROTOTYPE) { CHECKOP(0); if (!fr->fors.st) abort_interp("Internal error; FOR stack underflow."); if (fr->fors.st->end.type == PROG_ARRAY) { stk_array *arr = fr->fors.st->end.data.array; if (fr->fors.st->didfirst) { result = array_next(arr, &fr->fors.st->cur); } else { result = array_first(arr, &fr->fors.st->cur); fr->fors.st->didfirst = 1; } if (result) { array_data *val = array_getitem(arr, &fr->fors.st->cur); if (val) { CHECKOFLOW(2); PushInst(&fr->fors.st->cur); /* push key onto stack */ PushInst(val); /* push value onto stack */ tmp = 1; /* tell following IF to NOT branch out of loop */ } else { tmp = 0; /* tell following IF to branch out of loop */ } } else { fr->fors.st->cur.type = PROG_INTEGER; fr->fors.st->cur.line = 0; fr->fors.st->cur.data.number = 0; tmp = 0; /* tell following IF to branch out of loop */ } } else { int cur = fr->fors.st->cur.data.number; int end = fr->fors.st->end.data.number; tmp = fr->fors.st->step > 0; if (tmp) tmp = !(cur > end); else tmp = !(cur < end); if (tmp) { CHECKOFLOW(1); result = cur; fr->fors.st->cur.data.number += fr->fors.st->step; PushInt(result); } } CHECKOFLOW(1); PushInt(tmp); } void prim_forpop(PRIM_PROTOTYPE) { CHECKOP(0); if (!(fr->fors.top)) abort_interp("Internal error; FOR stack underflow."); CLEAR(&fr->fors.st->cur); CLEAR(&fr->fors.st->end); if (fr->trys.st) fr->trys.st->for_count--; fr->fors.top--; fr->fors.st = pop_for(fr->fors.st); } struct tryvars *push_try(struct tryvars *); struct tryvars *pop_try(struct tryvars *); void prim_trypop(PRIM_PROTOTYPE) { CHECKOP(0); if (!(fr->trys.top)) abort_interp("Internal error; TRY stack underflow."); fr->trys.top--; fr->trys.st = pop_try(fr->trys.st); } void prim_reverse(PRIM_PROTOTYPE) { int i; CHECKOP(1); oper1 = POP(); if (oper1->type != PROG_INTEGER) abort_interp("Invalid argument type."); tmp = oper1->data.number; /* Depth on stack */ if (tmp < 0) abort_interp("Argument must be positive."); CHECKOP(tmp); if (tmp > 0) { for (i = 0; i < (tmp / 2); i++) { temp2 = arg[*top - (tmp - i)]; arg[*top - (tmp - i)] = arg[*top - (i + 1)]; arg[*top - (i + 1)] = temp2; } } CLEAR(oper1); } void prim_lreverse(PRIM_PROTOTYPE) { int i; CHECKOP(1); oper1 = POP(); if (oper1->type != PROG_INTEGER) abort_interp("Invalid argument type."); tmp = oper1->data.number; /* Depth on stack */ if (tmp < 0) abort_interp("Argument must be positive."); CHECKOP(tmp); if (tmp > 0) { for (i = 0; i < (tmp / 2); i++) { temp2 = arg[*top - (tmp - i)]; arg[*top - (tmp - i)] = arg[*top - (i + 1)]; arg[*top - (i + 1)] = temp2; } } CLEAR(oper1); PushInt(tmp); }