/* * Copyright (C) 1995-1997 Christopher D. Granz * * This header may not be removed. * * Refer to the file "License" included in this package for further * information and before using any of the following. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "emi.h" /* * Prototypes */ static bool compare_instr ( register byte ** ); /* * Globals */ EM_VALUE * pStack; EM_VALUE * pStackEnd; EM_VALUE * pInterpStackPos; EM_VALUE * pVarStackBegin; EM_VALUE * pVarStackPos; #if 0 EM_VALUE * pStaticVars; EM_VALUE * pStaticVarsEnd; EM_VALUE * pStaticVarsPos; #endif /* * Functions */ void stack_init( void ) { if ( iInterpStackSize > 0 || iVarStackSize > 0 ) { pStack = alloc_mem( sizeof( EM_VALUE ) * ( iInterpStackSize + iVarStackSize ) ); pStackEnd = ( pStack + iInterpStackSize + iVarStackSize - 1 ); pInterpStackPos = pStack; pVarStackBegin = ( pStack + iInterpStackSize ); pVarStackPos = pVarStackBegin; } } void stack_free( void ) { interp_stack_pop( ( pInterpStackPos - pStack ) ); var_stack_pop( ( pVarStackPos - pVarStackBegin ) ); if ( pStack != NULL ) { free_mem( (void **) &pStack ); pStackEnd = NULL; pInterpStackPos = NULL; pVarStackBegin = NULL; pVarStackPos = NULL; } } #ifdef DEBUG static char *_get_valtype( int i ) { switch ( i ) { case TYPE_INT : return ( "integer" ); case TYPE_FLOAT : return ( "floating point" ); case TYPE_STRING: return ( "string" ); case TYPE_OBJECT: return ( "object" ); case TYPE_ARRAY : return ( "array" ); default : break; } return ( "unknown" ); } #endif /* * Push a new variable onto the variable stack. */ void var_stack_push( register EM_VALUE *p ) { if ( var_stack_overflow( ) == TRUE ) { strcpy( cErrorBuf, "Interpreter: Variable stack overflow." ); /* print_error( ); */ em_abort( ); } *pVarStackPos++ = *p; } /* * Pop (delete) a number of variables off the variable stack. */ void var_stack_pop( register short int rsiNumber ) { while ( rsiNumber-- > 0 ) delete_value( --pVarStackPos ); } /* * Reset the variable stack. */ void var_stack_reset( void ) { while ( pVarStackPos >= pVarStackBegin ) delete_value( pVarStackPos-- ); pVarStackPos = pVarStackBegin; } /* * Check for overflow in the variable stack. */ bool var_stack_overflow( void ) { if ( pVarStackPos > pStackEnd ) return ( TRUE ); else return ( FALSE ); } /* * Push a new value onto the interpreter stack. */ void interp_stack_push( register EM_VALUE *p ) { if ( interp_stack_overflow( ) == TRUE ) { strcpy( cErrorBuf, "Interpreter: Interpreter stack overflow." ); /* print_error( ); */ em_abort( ); } *pInterpStackPos++ = *p; } /* * Pop a number of values off the interpreter stack. */ void interp_stack_pop( register short int rsiNumber ) { while ( rsiNumber-- > 0 ) delete_value( --pInterpStackPos ); } /* * Check for overflow in the interpreter stack. */ bool interp_stack_overflow( void ) { if ( pInterpStackPos > pVarStackBegin ) return ( TRUE ); else return ( FALSE ); } void init_value( register EM_VALUE *p ) { switch ( p->iType ) { case TYPE_STRING : p->u.pString = alloc_mem( sizeof( EM_STRING ) ); p->u.pString->pString = EMPTY_STRING; break; case TYPE_OBJECT : p->u.pObject = alloc_mem( sizeof( EM_OBJECT ) ); p->u.pObject->pRealObject = NULL; break; default : break; } } void delete_value( register EM_VALUE *p ) { switch ( p->iType ) { case TYPE_STRING : str_free( p->u.pString->pString ); free_mem( (void **) &p->u.pString ); break; case TYPE_ARRAY : array_free( p->u.pArray ); break; case TYPE_OBJECT : free_mem( (void **) &p->u.pObject ); break; default : break; } } /* * Macros for getting values (constants) off an instruction byte stream. */ #define GET_INT1( p ) ( *( (byte *) (p) ) ) #define GET_INT2( p ) ( *( (i2 *) (p) ) ) #define GET_INT4( p ) ( *( (i4 *) (p) ) ) #define GET_FLOAT4( p ) ( *( (f4 *) (p) ) ) #define GET_FLOAT8( p ) ( *( (f8 *) (p) ) ) /* * Macros for moving instruction byte stream pointers. */ #define MOVEP_1( p ) ( (p)++ ) #define MOVEP_2( p ) ( (p) += sizeof( i2 ) ) #define MOVEP_4( p ) ( (p) += sizeof( i4 ) ) #define MOVEP_8( p ) ( (p) += sizeof( f8 ) ) /* * Main interpreter function; executes one instruction byte code. */ #define p2 ( pInterpStackPos - 1 ) byte *interpret_instruction( byte *pInstrStream ) { EM_VALUE v = { 0, { NULL } }; register EM_VALUE *p; /* register EM_ARRAY *pArray; */ register long rl = 0L; register long rl2 = 0L; switch ( *pInstrStream++ ) { case INSTR_END : break; case INSTR_NEW_INT : v.iType = TYPE_INT; var_stack_push( &v ); break; case INSTR_NEW_FLOAT : v.iType = TYPE_FLOAT; var_stack_push( &v ); break; case INSTR_NEW_STRING : v.iType = TYPE_STRING; init_value( &v ); var_stack_push( &v ); break; case INSTR_NEW_OBJECT : v.iType = TYPE_OBJECT; init_value( &v ); var_stack_push( &v ); break; case INSTR_NEW_ARRAY : v.iType = TYPE_ARRAY; v.u.pArray = NULL; var_stack_push( &v ); break; case INSTR_PUSH_ZERO : v.iType = TYPE_INT; v.u.lInt = 0L; interp_stack_push( &v ); break; case INSTR_PUSH_ONE : v.iType = TYPE_INT; v.u.lInt = 1L; interp_stack_push( &v ); break; case INSTR_PUSH_INT1 : v.iType = TYPE_INT; v.u.lInt = GET_INT1( pInstrStream ); MOVEP_1( pInstrStream ); interp_stack_push( &v ); break; case INSTR_PUSH_INT2 : v.iType = TYPE_INT; v.u.lInt = GET_INT2( pInstrStream ); MOVEP_2( pInstrStream ); interp_stack_push( &v ); break; case INSTR_PUSH_INT4 : v.iType = TYPE_INT; v.u.lInt = GET_INT4( pInstrStream ); MOVEP_4( pInstrStream ); interp_stack_push( &v ); break; case INSTR_PUSH_FLOAT4 : v.iType = TYPE_FLOAT; v.u.dFloat = GET_FLOAT4( pInstrStream ); MOVEP_4( pInstrStream ); interp_stack_push( &v ); break; case INSTR_PUSH_FLOAT8 : v.iType = TYPE_FLOAT; v.u.dFloat = GET_FLOAT8( pInstrStream ); MOVEP_8( pInstrStream ); interp_stack_push( &v ); break; case INSTR_PUSH_STRING : /* First we need to get the string length. */ rl = GET_INT2( pInstrStream ); MOVEP_2( pInstrStream ); v.iType = TYPE_STRING; init_value( &v ); v.u.pString->sLength = rl; v.u.pString->pString = alloc_mem( rl + 1 ); for ( rl2 = 0L; rl2 < rl; rl2++, MOVEP_1( pInstrStream ) ) (v.u.pString->pString)[rl2] = GET_INT1( pInstrStream ); (v.u.pString->pString)[rl2] = '\0'; interp_stack_push( &v ); break; case INSTR_PUSH_LOCAL : rl = GET_INT2( pInstrStream ); MOVEP_2( pInstrStream ); switch ( ( p = ( pVarStackBegin + rl ) )->iType ) { case TYPE_INT : v.iType = TYPE_INT; v.u.lInt = p->u.lInt; break; case TYPE_FLOAT : v.iType = TYPE_FLOAT; v.u.dFloat = p->u.dFloat; break; case TYPE_STRING : v.iType = TYPE_STRING; init_value( &v ); v.u.pString->sLength = p->u.pString->sLength; v.u.pString->pString = str_dup( p->u.pString->pString ); break; case TYPE_OBJECT : v.iType = TYPE_OBJECT; init_value( &v ); *v.u.pObject = *p->u.pObject; break; case TYPE_ARRAY : v.iType = TYPE_ARRAY; v.u.pArray = array_copy( p->u.pArray ); break; } interp_stack_push( &v ); break; case INSTR_PUSH_INDEX : p = ( pInterpStackPos - 2 ); if ( p->u.pArray == NULL ) { strcpy( cErrorBuf, "Interpreter: " "Attemp to access non-existent array." ); /* print_error( ); */ em_abort( ); } if ( ( rl = p2->u.lInt ) < 0 || rl >= p->u.pArray->sSize ) { strcpy( cErrorBuf, "Interpreter: " "Attemp to access non-existent array element." ); /* print_error( ); */ em_abort( ); } switch ( ( p->u.pArray->pElements + rl )->iType ) { case TYPE_INT : v.iType = TYPE_INT; v.u.lInt = ( p->u.pArray->pElements + rl )->u.lInt; break; case TYPE_FLOAT : v.iType = TYPE_FLOAT; v.u.dFloat = ( p->u.pArray->pElements + rl )->u.dFloat; break; case TYPE_STRING : v.iType = TYPE_STRING; init_value( &v ); v.u.pString->sLength = ( p->u.pArray->pElements + rl )->u.pString->sLength; v.u.pString->pString = str_dup( ( p->u.pArray->pElements + rl )->u.pString->pString ); break; case TYPE_OBJECT : v.iType = TYPE_OBJECT; init_value( &v ); *v.u.pObject = *( p->u.pArray->pElements + rl )->u.pObject; break; case TYPE_ARRAY : v.iType = TYPE_ARRAY; v.u.pArray = array_copy( ( p->u.pArray->pElements + rl )->u.pArray ); break; } interp_stack_pop( 2 ); interp_stack_push( &v ); break; case INSTR_POP : interp_stack_pop( 1 ); break; case INSTR_ASSIGN_LOCAL : rl = GET_INT2( pInstrStream ); MOVEP_2( pInstrStream ); switch ( ( p = ( pVarStackBegin + rl ) )->iType ) { case TYPE_INT : p->u.lInt = p2->u.lInt; break; case TYPE_FLOAT : p->u.dFloat = p2->u.dFloat; break; case TYPE_STRING : str_free( p->u.pString->pString ); p->u.pString->sLength = p2->u.pString->sLength; p->u.pString->pString = str_dup( p2->u.pString->pString ); break; case TYPE_OBJECT : *p->u.pObject = *p2->u.pObject; break; case TYPE_ARRAY : delete_value( p ); p->u.pArray = array_copy( p2->u.pArray ); break; } interp_stack_pop( 1 ); break; case INSTR_ASSIGN_INDEX : p = ( pInterpStackPos - 3 ); if ( p->u.pArray == NULL ) { strcpy( cErrorBuf, "Interpreter: " "Attemp to access non-existent array." ); /* print_error( ); */ em_abort( ); } if ( ( rl = ( pInterpStackPos - 2 )->u.lInt ) < 0 || rl >= p->u.pArray->sSize ) { strcpy( cErrorBuf, "Interpreter: " "Attemp to access non-existent array element." ); /* print_error( ); */ em_abort( ); } switch ( ( p->u.pArray->pElements + rl )->iType ) { case TYPE_INT : ( p->u.pArray->pElements + rl )->u.lInt = p2->u.lInt; break; case TYPE_FLOAT : ( p->u.pArray->pElements + rl )->u.dFloat = p2->u.dFloat; break; case TYPE_STRING : str_free( ( p->u.pArray->pElements + rl )->u.pString->pString ); ( p->u.pArray->pElements + rl )->u.pString->sLength = p2->u.pString->sLength; ( p->u.pArray->pElements + rl )->u.pString->pString = str_dup( p2->u.pString->pString ); break; case TYPE_OBJECT : *( p->u.pArray->pElements + rl )->u.pObject = *p2->u.pObject; break; case TYPE_ARRAY : delete_value( ( p->u.pArray->pElements + rl ) ); ( p->u.pArray->pElements + rl )->u.pArray = array_copy( p2->u.pArray ); break; } interp_stack_pop( 2 ); break; case INSTR_MULTIPLY : switch ( ( p = ( pInterpStackPos - 2 ) )->iType ) { case TYPE_INT : p->u.lInt *= p2->u.lInt; break; case TYPE_FLOAT : p->u.dFloat *= p2->u.dFloat; break; } interp_stack_pop( 1 ); break; case INSTR_DIVIDE : switch ( ( p = ( pInterpStackPos - 2 ) )->iType ) { case TYPE_INT : p->u.lInt /= p2->u.lInt; break; case TYPE_FLOAT : p->u.dFloat /= p2->u.dFloat; break; } interp_stack_pop( 1 ); break; case INSTR_MODULUS : ( pInterpStackPos - 2 )->u.lInt %= p2->u.lInt; interp_stack_pop( 1 ); break; case INSTR_ADD : switch ( ( p = ( pInterpStackPos - 2 ) )->iType ) { case TYPE_INT : p->u.lInt += p2->u.lInt; break; case TYPE_FLOAT : p->u.dFloat += p2->u.dFloat; break; case TYPE_STRING : { char *pStr = p->u.pString->pString; char *pStr2 = p2->u.pString->pString; if ( pStr == EMPTY_STRING ) { p->u.pString->sLength = p2->u.pString->sLength; p->u.pString->pString = str_dup( pStr2 ); } else { p->u.pString->pString = realloc_mem( pStr, strlen( pStr ) + strlen( pStr2 ) + 1 ); strcat( pStr, pStr2 ); p->u.pString->sLength = strlen( pStr ); } } break; } interp_stack_pop( 1 ); break; case INSTR_SUBTRACT : switch ( ( p = ( pInterpStackPos - 2 ) )->iType ) { case TYPE_INT : p->u.lInt -= p2->u.lInt; break; case TYPE_FLOAT : p->u.dFloat -= p2->u.dFloat; break; } interp_stack_pop( 1 ); break; case INSTR_LSHIFT : ( pInterpStackPos - 2 )->u.lInt <<= p2->u.lInt; interp_stack_pop( 1 ); break; case INSTR_RSHIFT : ( pInterpStackPos - 2 )->u.lInt >>= p2->u.lInt; interp_stack_pop( 1 ); break; case INSTR_AND : ( pInterpStackPos - 2 )->u.lInt &= p2->u.lInt; interp_stack_pop( 1 ); break; case INSTR_XOR : ( pInterpStackPos - 2 )->u.lInt ^= p2->u.lInt; interp_stack_pop( 1 ); break; case INSTR_OR : ( pInterpStackPos - 2 )->u.lInt |= p2->u.lInt; interp_stack_pop( 1 ); break; case INSTR_ONES_COMPLEMENT : p2->u.lInt = ~( p2->u.lInt ); break; case INSTR_JUMP : pInstrStream += GET_INT2( pInstrStream ); MOVEP_2( pInstrStream ); break; case INSTR_COMPARE : rl = GET_INT2( pInstrStream ); MOVEP_2( pInstrStream ); if ( compare_instr( &pInstrStream ) == TRUE ) pInstrStream += rl; { EM_VALUE *pInterpSav = pInterpStackPos; EM_VALUE *pVarSav = pVarStackPos; while ( *pInstrStream != INSTR_END ) pInstrStream = interpret_instruction( pInstrStream ); pInstrStream++; /* Skip past END instruction. */ interp_stack_pop( ( pInterpStackPos - pInterpSav ) ); var_stack_pop( ( pVarStackPos - pVarSav ) ); } break; case INSTR_CALL_BUILTIN_FUNC : rl = p2->u.lInt; interp_stack_pop( 1 ); /* * Would be nice to do all this stuff in the error checker but * we can't because the function index is taken off the stack * and not off the instruction stream (thus it may be different * each time.) */ for ( rl2 = 0L; emefCFuncTable[rl2].pName != NULL; rl2++ ); if ( rl < 0 || rl >= rl2 ) { strcpy( cErrorBuf, "Interpreter: " "Attemp to call non-existent builtin function." ); /* print_error( ); */ em_abort( ); } rl2 = 0L; rl2 = GET_INT1( pInstrStream ); MOVEP_1( pInstrStream ); /* * If the number of arguments is less then 0, the builtin * function will deal with the type checking (this is so a * variable number of arguments is possible.) */ if ( emefCFuncTable[rl].iNumArgs < 0 ) { for ( rl2 = 0L; rl2 < emefCFuncTable[rl].iNumArgs; rl2++ ) MOVEP_1( pInstrStream ); } else { if ( rl2 != emefCFuncTable[rl].iNumArgs ) { strcpy( cErrorBuf, "Interpreter: Builtin " "function does not conform to predefined prototype." ); /* print_error( ); */ em_abort( ); } for ( rl2 = 0L; rl2 < emefCFuncTable[rl].iNumArgs; rl2++ ) { if ( emefCFuncTable[rl].iArgTypes[rl2] != (signed int) GET_INT1( pInstrStream ) ) { strcpy( cErrorBuf, "Interpreter: Builtin function " "does not conform to predefined prototype." ); /* print_error( ); */ em_abort( ); } MOVEP_1( pInstrStream ); } } rl2 = 0L; rl2 = GET_INT1( pInstrStream ); MOVEP_1( pInstrStream ); /* * If the number of return values is less then 0, the * builtin function will deal with the type checking (this * is so a variable number of return values is possible.) */ if ( emefCFuncTable[rl].iNumReturn < 0 ) { for ( rl2 = 0L; rl2 < emefCFuncTable[rl].iNumReturn; rl2++ ) MOVEP_1( pInstrStream ); } else { if ( rl2 != emefCFuncTable[rl].iNumReturn ) { strcpy( cErrorBuf, "Interpreter: Builtin " "function does not conform to predefined prototype." ); /* print_error( ); */ em_abort( ); } for ( rl2 = 0L; rl2 < emefCFuncTable[rl].iNumReturn; rl2++ ) { if ( emefCFuncTable[rl].iReturnTypes[rl2] != (signed int) GET_INT1( pInstrStream ) ) { strcpy( cErrorBuf, "Interpreter: Builtin function " "does not conform to predefined prototype." ); /* print_error( ); */ em_abort( ); } MOVEP_1( pInstrStream ); } } ( *emefCFuncTable[rl].pFunc ) ( ); break; case INSTR_CALL_FUNC : { EM_VALUE *pOldVarStackBegin; EM_VALUE *pOldVarStackPos; byte *pNewStream; /* * Make sure the function trying to be called actaully * exists. */ rl = p2->u.lInt; interp_stack_pop( 1 ); if ( rl < 0 || rl > siTopFuncIndex ) { strcpy( cErrorBuf, "Interpreter: " "Attemp to call non-existent user function." ); /* print_error( ); */ em_abort( ); } /* * Calling of dynamicly loaded functions from code is not * allowed. */ if ( ppFuncs[rl]->bDynamic == TRUE ) { strcpy( cErrorBuf, "Interpreter: " "Attemp to call dynamicly loaded user function." ); /* print_error( ); */ em_abort( ); } /* * Compare call to real function. We abort if they don't * match. */ rl2 = GET_INT1( pInstrStream ); MOVEP_1( pInstrStream ); if ( rl2 != ppFuncs[rl]->iNumArgs ) { strcpy( cErrorBuf, "Interpreter: User function " "does not conform to predefined prototype." ); /* print_error( ); */ em_abort( ); } for ( rl2 = 0L; rl2 < ppFuncs[rl]->iNumArgs; rl2++ ) { if ( ppFuncs[rl]->pArgTypes[rl2] != (signed int) GET_INT1( pInstrStream ) ) { strcpy( cErrorBuf, "Interpreter: User function " "does not conform to predefined prototype." ); /* print_error( ); */ em_abort( ); } MOVEP_1( pInstrStream ); } rl2 = 0L; rl2 = GET_INT1( pInstrStream ); MOVEP_1( pInstrStream ); if ( rl2 != ppFuncs[rl]->iNumReturn ) { strcpy( cErrorBuf, "Interpreter: User function " "does not conform to predefined prototype." ); /* print_error( ); */ em_abort( ); } for ( rl2 = 0L; rl2 < ppFuncs[rl]->iNumReturn; rl2++ ) { if ( ppFuncs[rl]->pReturnTypes[rl2] != (signed int) GET_INT1( pInstrStream ) ) { strcpy( cErrorBuf, "Interpreter: User function " "does not conform to predefined prototype." ); /* print_error( ); */ em_abort( ); } MOVEP_1( pInstrStream ); } /* * We shift the variable stack so variable references * in the called function's code end up referencing the * right place. */ pOldVarStackBegin = pVarStackBegin; pOldVarStackPos = pVarStackPos; pVarStackBegin = pVarStackPos; pNewStream = ppFuncs[rl]->pInstrStream; while ( *pNewStream != INSTR_END ) pNewStream = interpret_instruction( pNewStream ); var_stack_pop( ( pVarStackPos - pOldVarStackPos ) ); pVarStackBegin = pOldVarStackBegin; pVarStackPos = pOldVarStackPos; } break; /* * If the error checker worked right and a bad pointer never * currupted the byte stream, this should NEVER happen. */ default : sprintf( cErrorBuf, "Interpreter: Illegal instruction (0x%.2X).", (int) *--pInstrStream ); print_error( ); *pInstrStream = INSTR_END; /* Don't let this happen again. */ /* em_abort( ); */ break; } return ( pInstrStream ); } #undef p2 /* * Compares two values. */ #define p ( pInterpStackPos - 1 ) #define p2 ( pInterpStackPos - 2 ) static bool compare_instr( register byte **ppInstrStream ) { register bool rbTrue = FALSE; switch ( p->iType ) { case TYPE_INT : switch ( *( *ppInstrStream++ ) ) { case COND_EQUAL : if ( p2->u.lInt == p->u.lInt ) rbTrue = TRUE; break; case COND_NOT_EQUAL : if ( p2->u.lInt != p->u.lInt ) rbTrue = TRUE; break; case COND_GREATER : if ( p2->u.lInt > p->u.lInt ) rbTrue = TRUE; break; case COND_LESS : if ( p2->u.lInt < p->u.lInt ) rbTrue = TRUE; break; case COND_GREATER_OR_EQUAL: if ( p2->u.lInt >= p->u.lInt ) rbTrue = TRUE; break; case COND_LESS_OR_EQUAL : if ( p2->u.lInt <= p->u.lInt ) rbTrue = TRUE; break; } break; case TYPE_FLOAT : switch ( *( *ppInstrStream++ ) ) { case COND_EQUAL : if ( p2->u.dFloat == p->u.dFloat ) rbTrue = TRUE; break; case COND_NOT_EQUAL : if ( p2->u.dFloat != p->u.dFloat ) rbTrue = TRUE; break; case COND_GREATER : if ( p2->u.dFloat > p->u.dFloat ) rbTrue = TRUE; break; case COND_LESS : if ( p2->u.dFloat < p->u.dFloat ) rbTrue = TRUE; break; case COND_GREATER_OR_EQUAL: if ( p2->u.dFloat >= p->u.dFloat ) rbTrue = TRUE; break; case COND_LESS_OR_EQUAL : if ( p2->u.dFloat <= p->u.dFloat ) rbTrue = TRUE; break; } break; case TYPE_STRING: switch ( *( *ppInstrStream++ ) ) { case COND_EQUAL : if ( strcmp( p2->u.pString->pString, p->u.pString->pString ) == 0 ) rbTrue = TRUE; break; case COND_NOT_EQUAL : if ( strcmp( p2->u.pString->pString, p->u.pString->pString ) != 0 ) rbTrue = TRUE; break; } break; case TYPE_OBJECT: switch ( *( *ppInstrStream++ ) ) { case COND_EQUAL : if ( p2->u.pObject->pRealObject == p->u.pObject->pRealObject ) rbTrue = TRUE; break; case COND_NOT_EQUAL : if ( p2->u.pObject->pRealObject != p->u.pObject->pRealObject ) rbTrue = TRUE; break; } break; } interp_stack_pop( 2 ); return ( rbTrue ); } #undef p #undef p2 #undef GET_INT1 #undef GET_INT2 #undef GET_INT4 #undef GET_FLOAT4 #undef GET_FLOAT8 #undef MOVEP_1 #undef MOVEP_2 #undef MOVEP_4 #undef MOVEP_8 /* * End of interp.c */