Example #1
0
 void lib_strcat(Machine& machine)
 {
     std::string s2 = PopString(machine);
     std::string s1 = PopString(machine);
     std::string r = s1 + s2;
     machine.stack.Push(r);
 }
Example #2
0
 void lib_strsplit(Machine& machine)
 {
     std::string delim = PopString(machine);
     std::string s = PopString(machine);
     const char *p = s.c_str();
     std::stringstream strm;
     std::vector<Data> items;
     while (*p)
     {
         if (*p == delim[0])
         {
             items.push_back(DataObj::CreateString(strm.str()));
             strm.str("");
         }
         else
         {
             strm << *p;
         }
         ++p;
     }
     if (strm.str().size() > 0)
     {
         items.push_back(DataObj::CreateString(strm.str()));
         strm.str();
     }
     machine.stack.Push(items);
 }
Example #3
0
void Std_Fopen(VM* vm)
{
	const char* filename = PopString(vm);
	const char* mode = PopString(vm);
	
	FILE* file = fopen(filename, mode);
	if(!file)
	{	
		PushObject(vm, &NullObject);
		return;
	}
	
	PushNative(vm, file, Std_Fclose, NULL);
}
Example #4
0
 void lib_strcmp(Machine& machine)
 {
     int ignoreCase = PopInt(machine);
     std::string a2 = PopString(machine);
     std::string a1 = PopString(machine);
     int n = 0;
     if (ignoreCase)
     {
         n = _stricmp(a1.c_str(), a2.c_str());
     }
     else
     {
         n = ::strcmp(a1.c_str(), a2.c_str());
     }
     machine.stack.Push(n);
 }
Example #5
0
void Std_Printf(VM* vm)
{
	const char* format = PopString(vm);
	int len = strlen(format);
	
	for(int i = 0; i < len; ++i)
	{
		int c = format[i];
		
		switch(c)
		{
			case '%':
			{
				int type = format[++i];
				switch(type)
				{
					case 'g':
					{
						printf("%g", PopNumber(vm));
					} break;
					
					case 's':
					{
						printf("%s", PopString(vm));
					} break;
					
					case 'c':
					{
						printf("%c", (int)PopNumber(vm));
					} break;

					case 'o':
					{
						WriteObject(vm, PopObject(vm));
					} break;
					
					default:
						putc(c, stdout);
						putc(type, stdout);
				}
			} break;
			
			default:
				putc(c, stdout);
		}
	}
}
Example #6
0
void Std_Strcat(VM* vm)
{
	const char* a = PopString(vm);
	const char* b = PopString(vm);
	
	int la = strlen(a);
	int lb = strlen(b);
	
	char* newString = alloca(la + lb + 1);
	if(!newString)
	{
		fprintf(stderr, "Out of stack space when alloca'ing\n");
		exit(1);
	}
	strcpy(newString, a);
	strcpy(newString + la, b);
	
	PushString(vm, newString);
}
/**
 * @brief Called when user changes selection in left/middle/right path's combo box.
 */
void COpenView::OnSelchangeCombo(int index) 
{
	int sel = m_ctlPath[index].GetCurSel();
	if (sel != CB_ERR)
	{
		m_ctlPath[index].GetLBText(sel, PopString(m_strPath[index]));
		m_ctlPath[index].SetWindowText(m_strPath[index].c_str());
		UpdateData(TRUE);
	}
	UpdateButtonStates();
}
Example #8
0
 void lib_substr(Machine& machine)
 {
     int len = PopInt(machine);
     int startPos = PopInt(machine);
     std::string s = PopString(machine);
     if (len == 0)
     {
         len = std::string::npos;
     }
     std::string r = s.substr(startPos, len);
     machine.stack.Push(r);
 }
Example #9
0
void Std_Assert(VM* vm)
{
	int result = (int)PopNumber(vm);
	const char* message = PopString(vm);
	
	if(!result)
	{
		fprintf(stderr, "Assertion failed (pc %i)\n", vm->pc);
		fprintf(stderr, "%s\n", message);
		exit(1);
	}
}
Example #10
0
/*
 * Manual page at function.def
 */
INT16 CGEN_PROTECTED CFunction::OnRestore()
{
  // Delegate to running function                                               // ------------------------------------
  FNC_DELEGATE OnRestore();                                                     // Use a weird macro (see function.def)

  // Get arguments                                                              // ------------------------------------
  CDlpObject* iInst = GetActiveInstance();                                      // Get first instance argument
  if (m_nStackLen<=0)                                                           // Stack at least to contain file name
    return IERROR(this,FNC_STACKUNDERFLOW," on method ","-restore",0);          //   Not so? Error!
  if (m_aStack[0].nType==T_INSTANCE) PopInstance(1);                            // Pop the active instance
  const char* lpsFilename = PopString(2);                                       // Get the file name

  // Prevent from restoring other than the root function                        // ------------------------------------
  if (iInst && iInst->IsKindOf("function") && iInst!=GetRootFnc())              // Restoring a function other than root
    return IERROR(this,FNC_NOTALLOWED,"-restore","for non-root functions",0);   //   No go!

  // Do restore                                                                 // ------------------------------------
  INT16 nFormat = 0;                                                            // The file format
  if (m_bXml) nFormat |= SV_XML;                                                // /xml selected
  if (m_bDn3) nFormat |= SV_DN3;                                                // /dn3 selected
  if (m_bZip) nFormat |= SV_ZIP;                                                // /zip selected
  return CDlpObject_Restore(iInst,lpsFilename,nFormat);                         // Restore the instance
}
Example #11
0
/*
 * Manual page at function.def
 */
INT16 CGEN_PUBLIC CFunction::OnSet()
{
  // Delegate to running function                                               // ------------------------------------
  FNC_DELEGATE OnSet();                                                         // Use a weird macro (see function.def)

  // Initialize                                                                 // ------------------------------------
  const char* lpsId = GetNextToken(TRUE);                                       // Determine field name
  CDlpObject* iCont = GetActiveInstance();                                      // Get first instance argument
  if (m_nStackLen<=0)                                                           // Stack is to contain the new value
    return IERROR(this,FNC_STACKUNDERFLOW," on method ","-set",0);              // |
  if (m_aStack[0].nType==T_INSTANCE && m_aStack[0].val.i==iCont) PopInstance(1);// Pop the active instance

  // Validate                                                                   // ------------------------------------
  if (!dlp_strlen(lpsId))                                                       // If no field name committed
    return IERROR(this,FNC_EXPECT,"field identifier after -set",0,0);           //   Error
  SWord* lpWrd = iCont->FindWord(lpsId,WL_TYPE_FIELD);                          // Find field in container
  if (!lpWrd)                                                                   // If not found
  {                                                                             // >>
    iCont = this;                                                               //   Use this instance as container
    lpWrd = FindWord(lpsId,WL_TYPE_FIELD);                                      //   And seek again
  }                                                                             // <<
  if (!lpWrd                  ) return IERROR(this,ERR_NOTFIELD,lpsId,0,0);     // If still not found --> error
  if (lpWrd->nFlags & FF_NOSET) return IERROR(this,FNC_NOSET   ,lpsId,0,0);     // If write-protected --> error

  // Set new value                                                              // ------------------------------------
  switch (lpWrd->ex.fld.nType)                                                  // Branch for field variable type
  {                                                                             // >>
    case T_BOOL    : {      BOOL bTmp=(   BOOL)PopLogic(2);   iCont->SetField(lpWrd,(void*)&bTmp); } break;// - Boolean
    case T_UCHAR   : {     UINT8 nTmp=(  UINT8)PopNumber(2).x;iCont->SetField(lpWrd,(void*)&nTmp); } break;// - Unsigned character
    case T_CHAR    : {      INT8 nTmp=(   INT8)PopNumber(2).x;iCont->SetField(lpWrd,(void*)&nTmp); } break;// - Signed character
    case T_USHORT  : {    UINT16 nTmp=( UINT16)PopNumber(2).x;iCont->SetField(lpWrd,(void*)&nTmp); } break;// - Unsigned short integer
    case T_SHORT   : {     INT16 nTmp=(  INT16)PopNumber(2).x;iCont->SetField(lpWrd,(void*)&nTmp); } break;// - Signed short integer
    case T_UINT    : {    UINT32 nTmp=( UINT32)PopNumber(2).x;iCont->SetField(lpWrd,(void*)&nTmp); } break;// - Unsigned integer
    case T_INT     : {     INT32 nTmp=(  INT32)PopNumber(2).x;iCont->SetField(lpWrd,(void*)&nTmp); } break;// - Signed integer
    case T_ULONG   : {    UINT64 nTmp=( UINT64)PopNumber(2).x;iCont->SetField(lpWrd,(void*)&nTmp); } break;// - Unsigned long integer
    case T_LONG    : {     INT64 nTmp=(  INT64)PopNumber(2).x;iCont->SetField(lpWrd,(void*)&nTmp); } break;// - Signed long integer
    case T_FLOAT   : {   FLOAT32 nTmp=(FLOAT32)PopNumber(2).x;iCont->SetField(lpWrd,(void*)&nTmp); } break;// - Single precision floating point
    case T_DOUBLE  : {   FLOAT64 nTmp=(FLOAT64)PopNumber(2).x;iCont->SetField(lpWrd,(void*)&nTmp); } break;// - Double precision floating point
    case T_COMPLEX : { COMPLEX64 nTmp=         PopNumber(2);  iCont->SetField(lpWrd,(void*)&nTmp); } break;// - Double precision complex floating point
    case T_TEXT    : /* Fall through */                                                                    // - Text (depreciated type!)
    case T_CSTRING : /* Fall through */                                                                    // - Constant string
    case T_STRING  : {   char* lpsTmp =        PopString(2);  iCont->SetField(lpWrd,(void*)&lpsTmp);}break;// - String
    case T_INSTANCE:                                                            // - Instance
    {                                                                           //   >>
      CDlpObject* iVal = PopInstance(2);                                        //     Get new value
      if                                                                        //     Check instance type
      (                                                                         //     |
        dlp_strlen(lpWrd->ex.fld.lpType)>0     &&                               //     - Typed instance field?
        iVal != NULL                           &&                               //     - New value non-NULL?
        OfKind(lpWrd->ex.fld.lpType,iVal)==NULL                                 //     - Type cast impossible?
      )                                                                         //     |
      {                                                                         //     >>
        return                                                                  //       Error
          IERROR(this,FNC_TYPECAST,0,iVal->m_lpClassName,lpWrd->ex.fld.lpType); //       |
      }                                                                         //     <<
      iCont->SetField(lpWrd,(void*)&iVal);                                      //     Set new value
      break;                                                                    //     .
    }                                                                           //   <<
    default:                                                                    // - Other types
      if (lpWrd->ex.fld.nType>0 && lpWrd->ex.fld.nType<=256) {                  //     Character array?
        char* lpsTmp=PopString(2);                                              //       Set new value
        iCont->SetField(lpWrd,&lpsTmp);                                         //       |
      } else                                                                    //     Type unknown!
        DLPASSERT(FMSG("Unknown field type"));                                  //       Error
  }                                                                             // <<

  return O_K;                                                                   // Done.
}
Example #12
0
void Std_Tonumber(VM* vm)
{
	const char* string = PopString(vm);
	PushNumber(vm, strtod(string, NULL));
}
Example #13
0
void ExecuteCycle(VM* vm)
{
	if(vm->pc == -1) return;
	if(vm->debug)
		printf("pc %i: ", vm->pc);
	
	if(vm->stackSize < vm->numGlobals)
		printf("Global(s) were removed from the stack!\n");
	
	switch(vm->program[vm->pc])
	{
		case OP_PUSH_NULL:
		{
			if(vm->debug)
				printf("push_null\n");
			++vm->pc;
			PushObject(vm, &NullObject);
		} break;
		
		case OP_PUSH_NUMBER:
		{
			if(vm->debug)
				printf("push_number\n");
			++vm->pc;
			int index = ReadInteger(vm);
			PushNumber(vm, vm->numberConstants[index]);
		} break;
		
		case OP_PUSH_STRING:
		{
			if(vm->debug)
				printf("push_string\n");
			++vm->pc;
			int index = ReadInteger(vm);
			PushString(vm, vm->stringConstants[index]);
		} break;
		
		case OP_PUSH_FUNC:
		{
			if(vm->debug)
				printf("push_func\n");
			Word hasEllipsis = vm->program[++vm->pc];
			Word isExtern = vm->program[++vm->pc];
			Word numArgs = vm->program[++vm->pc];
			++vm->pc;
			int index = ReadInteger(vm);
			
			PushFunc(vm, index, hasEllipsis, isExtern, numArgs);
		} break;
		
		case OP_PUSH_DICT:
		{
			if(vm->debug)
				printf("push_dict\n");
			++vm->pc;
			PushDict(vm);
		} break;

		case OP_CREATE_DICT_BLOCK:
		{
			if(vm->debug)
				printf("create_dict_block\n");
			++vm->pc;
			int length = ReadInteger(vm);
			Object* obj = PushDict(vm);
			if(length > 0)
			{
				// stack (before dict) is filled with key-value pairs (backwards, key is higher on stack)
				for(int i = 0; i < length * 2; i += 2)
					DictPut(&obj->dict, vm->stack[vm->stackSize - i - 2]->string.raw, vm->stack[vm->stackSize - i - 3]);
				vm->stackSize -= length * 2;
				vm->stack[vm->stackSize - 1] = obj;
			}
		} break;

		case OP_CREATE_ARRAY:
		{
			if(vm->debug)
				printf("create_array\n");
			++vm->pc;
			int length = (int)PopNumber(vm);
			PushArray(vm, length);
		} break;
		
		case OP_CREATE_ARRAY_BLOCK:
		{
			if(vm->debug)
				printf("create_array_block\n");
			++vm->pc;
			int length = ReadInteger(vm);
			Object* obj = PushArray(vm, length);
			if(length > 0)
			{
				for(int i = 0; i < length; ++i)
					obj->array.members[length - i - 1] = vm->stack[vm->stackSize - 2 - i];
				vm->stackSize -= length;
				vm->stack[vm->stackSize - 1] = obj;
			}
		} break;

		case OP_LENGTH:
		{
			if(vm->debug)
				printf("length\n");
			++vm->pc;
			Object* obj = PopObject(vm);
			if(obj->type == OBJ_STRING)
				PushNumber(vm, strlen(obj->string.raw));
			else if(obj->type == OBJ_ARRAY)
				PushNumber(vm, obj->array.length);
			else
			{
				fprintf(stderr, "Attempted to get length of %s\n", ObjectTypeNames[obj->type]);
				exit(1);
			}
		} break;
		
		case OP_ARRAY_PUSH:
		{
			if(vm->debug)
				printf("array_push\n");
			++vm->pc;
			
			Object* obj = PopArrayObject(vm);
			Object* value = PopObject(vm);

			while(obj->array.length + 1 >= obj->array.capacity)
			{
				obj->array.capacity *= 2;
				obj->array.members = erealloc(obj->array.members, obj->array.capacity * sizeof(Object*));
			}
			
			obj->array.members[obj->array.length++] = value;
		} break;
		
		case OP_ARRAY_POP:
		{
			if(vm->debug)
				printf("array_pop\n");
			++vm->pc;
			Object* obj = PopArrayObject(vm);
			if(obj->array.length <= 0)
			{
				fprintf(stderr, "Cannot pop from empty array\n");
				exit(1);
			}
			
			PushObject(vm, obj->array.members[--obj->array.length]);
		} break;
		
		case OP_ARRAY_CLEAR:
		{
			if(vm->debug)
				printf("array_clear\n");
			++vm->pc;
			Object* obj = PopArrayObject(vm);
			obj->array.length = 0;
		} break;

		case OP_DICT_SET:
		{
			if(vm->debug)
				printf("dict_set\n");
			++vm->pc;
			Object* obj = PopDict(vm);
			const char* key = PopString(vm);
			Object* value = PopObject(vm);
			
			DictPut(&obj->dict, key, value);
		} break;
		
		case OP_DICT_GET:
		{
			if(vm->debug)
				printf("dict_get\n");
			++vm->pc;
			Object* obj = PopDict(vm);
			const char* key = PopString(vm);
			
			Object* value = DictGet(&obj->dict, key);
			if(value)
				PushObject(vm, value);
			else
				PushObject(vm, &NullObject);
		} break;

		case OP_DICT_PAIRS:
		{
			if(vm->debug)
				printf("dict_pairs\n");
			++vm->pc;
			Object* obj = PopDict(vm);
			Object* aobj = PushArray(vm, obj->dict.numEntries);
			
			int len = 0;
			for(int i = 0; i <= obj->dict.capacity; ++i)
			{
				DictNode* node = obj->dict.buckets[i];
				while(node)
				{
					Object* pair = PushArray(vm, 2);
					
					Object* key = NewObject(vm, OBJ_STRING);
					key->string.raw = estrdup(node->key);
					
					pair->array.members[0] = key;
					pair->array.members[1] = node->value;
					
					aobj->array.members[len++] = PopObject(vm);
					
					node = node->next;
				}
			}
		} break;
		
		#define BIN_OP_TYPE(op, operator, type) case OP_##op: { if(vm->debug) printf("%s\n", #op); Object* val2 = PopObject(vm); Object* val1 = PopObject(vm); PushNumber(vm, (type)val1->number operator (type)val2->number); ++vm->pc; } break;
		#define BIN_OP(op, operator) BIN_OP_TYPE(op, operator, double)
		
		BIN_OP(ADD, +)
		BIN_OP(SUB, -)
		BIN_OP(MUL, *)
		BIN_OP(DIV, /)
		BIN_OP_TYPE(MOD, %, int)
		BIN_OP_TYPE(OR, |, int)
		BIN_OP_TYPE(AND, &, int)
		BIN_OP(LT, <)
		BIN_OP(LTE, <=)
		BIN_OP(GT, >)
		BIN_OP(GTE, >=)
		BIN_OP_TYPE(LOGICAL_AND, &&, int)
		BIN_OP_TYPE(LOGICAL_OR, ||, int)
		
		#define CBIN_OP(op, operator) case OP_##op: { ++vm->pc; if(vm->debug) printf("%s\n", #op); Object* b = PopObject(vm); Object* a = PopObject(vm); a->number operator b->number; } break;
		
		CBIN_OP(CADD, +=)
		CBIN_OP(CSUB, -=)
		CBIN_OP(CMUL, *=)
		CBIN_OP(CDIV, /=)
		
		case OP_EQU:
		{
			++vm->pc;
			Object* o2 = PopObject(vm);
			Object* o1 = PopObject(vm);
			
			if(o1->type != o2->type) PushNumber(vm, 0);
			else
			{
				if(o1->type == OBJ_STRING) { PushNumber(vm, strcmp(o1->string.raw, o2->string.raw) == 0); }
				else if(o1->type == OBJ_NUMBER) { PushNumber(vm, o1->number == o2->number); }
				else PushNumber(vm, o1 == o2);
			}
		} break;
		
		case OP_NEQU:
		{
			++vm->pc;
			Object* o2 = PopObject(vm);
			Object* o1 = PopObject(vm);
			
			if(o1->type != o2->type) PushNumber(vm, 1);
			else
			{
				if(o1->type == OBJ_STRING) { PushNumber(vm, strcmp(o1->string.raw, o2->string.raw) != 0); }
				else if(o1->type == OBJ_NUMBER) { PushNumber(vm, o1->number != o2->number); }
				else PushNumber(vm, o1 != o2);
			}
		} break;
		
		case OP_NEG:
		{
			if(vm->debug)
				printf("neg\n");
			
			++vm->pc;
			Object* obj = PopObject(vm);
			PushNumber(vm, -obj->number);
		} break;
		
		case OP_LOGICAL_NOT:
		{
			if(vm->debug)
				printf("not\n");
			
			++vm->pc;
			Object* obj = PopObject(vm);
			PushNumber(vm, !obj->number);
		} break;
		
		case OP_SETINDEX:
		{
			++vm->pc;

			Object* obj = PopObject(vm);
			Object* indexObj = PopObject(vm);
			Object* value = PopObject(vm);
			if(vm->debug)
				printf("setindex\n");
			
			if(obj->type == OBJ_ARRAY)
			{
				if(indexObj->type != OBJ_NUMBER)
				{
					fprintf(stderr, "Attempted to index array with a %s (expected number)\n", ObjectTypeNames[indexObj->type]);
					exit(1);
				}
				
				int index = (int)indexObj->number;
				
				int arrayLength = obj->array.length;
				Object** members = obj->array.members;
				
				if(index >= 0 && index < arrayLength)
					members[index] = value;
				else
				{
					fprintf(stderr, "Invalid array index %i\n", index);
					exit(1);
				}
			}
			else if(obj->type == OBJ_STRING)
			{				
				if(indexObj->type != OBJ_NUMBER)
				{
					fprintf(stderr, "Attempted to index string with a %s (expected number)\n", ObjectTypeNames[indexObj->type]);
					exit(1);
				}
				
				if(value->type != OBJ_NUMBER)
				{
					fprintf(stderr, "Attempted to assign a %s to an index of a string '%s' (expected number/character)\n", ObjectTypeNames[value->type], obj->string.raw);
					exit(1);
				}
				
				obj->string.raw[(int)indexObj->number] = (char)value->number;
			}
			else if(obj->type == OBJ_DICT)
			{
				if(indexObj->type != OBJ_STRING)
				{
					fprintf(stderr, "Attempted to index dict with a %s (expected string)\n", ObjectTypeNames[indexObj->type]);
					exit(1);
				}
				
				DictPut(&obj->dict, indexObj->string.raw, value);
			}
			else
			{
				fprintf(stderr, "Attempted to index a %s\n", ObjectTypeNames[obj->type]);
				exit(1);
			}
		} break;

		case OP_GETINDEX:
		{
			++vm->pc;

			Object* obj = PopObject(vm);
			Object* indexObj = PopObject(vm);

			if(obj->type == OBJ_ARRAY)
			{
				if(indexObj->type != OBJ_NUMBER)
				{
					fprintf(stderr, "Attempted to index array with a %s (expected number)\n", ObjectTypeNames[indexObj->type]);
					exit(1);
				}
				
				int index = (int)indexObj->number;
				
				int arrayLength = obj->array.length;
				Object** members = obj->array.members;
				
				if(index >= 0 && index < arrayLength)
				{
					if(members[index])
						PushObject(vm, members[index]);
					else
					{
						fprintf(stderr, "attempted to index non-existent value in array\n");
						exit(1);
					}
					if(vm->debug)
						printf("getindex %i\n", index);
				}
				else
				{
					fprintf(stderr, "Invalid array index %i\n", index);
					exit(1);
				}
			}
			else if(obj->type == OBJ_STRING)
			{
				if(indexObj->type != OBJ_NUMBER)
				{
					fprintf(stderr, "Attempted to index string with a %s (expected number)\n", ObjectTypeNames[indexObj->type]);
					exit(1);
				}
				
				PushNumber(vm, obj->string.raw[(int)indexObj->number]);
			}
			else if(obj->type == OBJ_DICT)
			{
				if(indexObj->type != OBJ_STRING)
				{
					fprintf(stderr, "Attempted to index dict with a %s (expected string)\n", ObjectTypeNames[indexObj->type]);
					exit(1);
				}
				
				Object* val = (Object*)DictGet(&obj->dict, indexObj->string.raw);
				if(val)
					PushObject(vm, val);
				else
					PushObject(vm, &NullObject);
			}
			else 
			{
				fprintf(stderr, "Attempted to index a %s\n", ObjectTypeNames[obj->type]);
				exit(1);
			}
		} break;

		case OP_SET:
		{
			++vm->pc;
			int index = ReadInteger(vm);
			
			Object* top = PopObject(vm);
			vm->stack[index] = top;
			
			if(vm->debug)
			{
				if(top->type == OBJ_NUMBER) printf("set %i to %g\n", index, top->number);
				else if(top->type == OBJ_STRING) printf("set %i to %s\n", index, top->string);	
			}
		} break;
		
		case OP_GET:
		{
			++vm->pc;
			int index = ReadInteger(vm);
			if(vm->stack[index])
				PushObject(vm, (vm->stack[index]));
			else
				PushObject(vm, &NullObject);
				
			if(vm->debug)
				printf("get %i\n", index);
		} break;
		
		case OP_WRITE:
		{
			if(vm->debug)
				printf("write\n");
			Object* top = PopObject(vm);
			WriteObject(vm, top);
			printf("\n");
			++vm->pc;
		} break;
		
		case OP_READ:
		{
			if(vm->debug)
				printf("read\n");
			char* string = ReadStringFromStdin();
			PushString(vm, string);
			free(string);
			++vm->pc;
		} break;
		
		case OP_GOTO:
		{
			++vm->pc;
			int pc = ReadInteger(vm);
			vm->pc = pc;
		
			if(vm->debug)
				printf("goto %i\n", vm->pc);
		} break;
		
		case OP_GOTOZ:
		{
			++vm->pc;
			int pc = ReadInteger(vm);
			
			Object* top = PopObject(vm);
			if(top->number == 0)
			{
				vm->pc = pc;
				if(vm->debug)
					printf("gotoz %i\n", vm->pc);
			}
		} break;
		
		case OP_CALL:
		{
			Word nargs = vm->program[++vm->pc];
			++vm->pc;
			int index = ReadInteger(vm);

			if(vm->debug)
				printf("call %s\n", vm->functionNames[index]);

			PushIndir(vm, nargs);

			vm->pc = vm->functionPcs[index];
		} break;
		
		case OP_CALLP:
		{
			Word hasEllipsis, isExtern, numArgs;
			Word nargs = vm->program[++vm->pc];
			++vm->pc;
			int id = PopFunc(vm, &hasEllipsis, &isExtern, &numArgs);
			
			if(vm->debug)
				printf("callp %s%s\n", isExtern ? "extern " : "", isExtern ? vm->externNames[id] : vm->functionNames[id]);
			
			if(isExtern)
				vm->externs[id](vm);
			else
			{
				if(!hasEllipsis)
				{
					if(nargs != numArgs)
					{
						fprintf(stderr, "Function '%s' expected %i args but recieved %i args\n", vm->functionNames[id], numArgs, nargs);
						exit(1);
					}
				}
				else
				{
					if(nargs < numArgs)
					{
						fprintf(stderr, "Function '%s' expected at least %i args but recieved %i args\n", vm->functionNames[id], numArgs, nargs);
						exit(1);
					}
				}
				
				if(!hasEllipsis) PushIndir(vm, nargs);
				else
				{
					// runtime collapsing of arguments:
					// the concrete arguments (known during compilation) are on the top of
					// the stack. We create an array (by pushing it and then decrementing the
					// stack pointer) and fill it up with the ellipsed arguments (behind the
					// concrete arguments). We then place this array just before the concrete
					// arguments in the stack so that it can be accessed as an argument "args".
					// The indirection info is pushed onto the indirection stack (the concrete and
					// non-concrete [aside from the one that the 'args' array replaces] are still 
					// present on the stack, so all of the arguments are to be removed)
					
					/*printf("args:\n");
					for(int i = 0; i < nargs; ++i)
					{
						WriteObject(vm, vm->stack[vm->stackSize - i - 1]);
						printf("\n");
					}
					printf("end\n");*/
					
					//printf("members:\n");
					Object* obj = PushArray(vm, nargs - numArgs);
					vm->stackSize -= 1;
					for(int i = 0; i < obj->array.length; ++i)
					{
						obj->array.members[i] = vm->stack[vm->stackSize - numArgs - 1 - i];
						/*WriteObject(vm, obj->array.members[i]);
						printf("\n");*/
					}
					//printf("end\n");
					
					vm->stack[vm->stackSize - numArgs - 1] = obj;
					
					/*printf("final args:\n");
					for(int i = 0; i < numArgs + 1; ++i)
					{
						WriteObject(vm, vm->stack[vm->stackSize - i - 1]);
						printf("\n");
					}
					printf("end\n");*/
					
					PushIndir(vm, nargs);
				}
				
				vm->pc = vm->functionPcs[id];
			}
		} break;
		
		case OP_RETURN:
		{
			if(vm->debug)
				printf("ret\n");
			PopIndir(vm);
		} break;
		
		case OP_RETURN_VALUE:
		{
			if(vm->debug)
				printf("retval\n");
			Object* returnValue = PopObject(vm);
			PopIndir(vm);
			PushObject(vm, returnValue);
		} break;
		
		case OP_CALLF:
		{
			++vm->pc;
			int index = ReadInteger(vm);
			if(vm->debug)
				printf("callf %s\n", vm->externNames[index]);
			vm->externs[index](vm);
		} break;

		case OP_GETLOCAL:
		{
			++vm->pc;
			int index = ReadInteger(vm);
			PushObject(vm, GetLocal(vm, index));
			if(vm->debug)
				printf("getlocal %i (fp: %i, stack size: %i)\n", index, vm->fp, vm->stackSize);
		} break;
		
		case OP_SETLOCAL:
		{
			if(vm->debug)
				printf("setlocal\n");
			++vm->pc;
			int index = ReadInteger(vm);
			SetLocal(vm, index, PopObject(vm));
		} break;
		
		case OP_HALT:
		{
			if(vm->debug)
				printf("halt\n");
			vm->pc = -1;
		} break;
		
		default:
			printf("Invalid instruction %i\n", vm->program[vm->pc]);
			break;
	}
}