Beispiel #1
0
STOID Mold_Error(REBVAL *value, REB_MOLD *mold, REBFLG molded)
{
	ERROR_OBJ *err;
	REBVAL *msg;  // Error message block

	// Protect against recursion. !!!!

	if (molded) {
		if (VAL_OBJ_FRAME(value) && VAL_ERR_NUM(value) >= RE_NOTE && VAL_ERR_OBJECT(value))
			Mold_Object(value, mold);
		else {
			// Happens if throw or return is molded.
			// make error! 0-3
			Pre_Mold(value, mold);
			Append_Int(mold->series, VAL_ERR_NUM(value));
			End_Mold(mold);
		}
		return;
	}

	// If it is an unprocessed BREAK, THROW, CONTINUE, RETURN:
	if (VAL_ERR_NUM(value) < RE_NOTE || !VAL_ERR_OBJECT(value)) {
		VAL_ERR_OBJECT(value) = Make_Error(VAL_ERR_NUM(value), value, 0, 0); // spoofs field
	}
	err = VAL_ERR_VALUES(value);

	// Form: ** <type> Error:
	Emit(mold, "** WB", &err->type, RS_ERRS+0);

	// Append: error message ARG1, ARG2, etc.
	msg = Find_Error_Info(err, 0);
	if (msg) {
		if (!IS_BLOCK(msg)) Mold_Value(mold, msg, 0);
		else {
			//start = DSP + 1;
			//Reduce_In_Frame(VAL_ERR_OBJECT(value), VAL_BLK_DATA(msg));
			//SERIES_TAIL(DS_Series) = DSP + 1;
			//Form_Block_Series(DS_Series, start, mold, 0);
			Form_Block_Series(VAL_SERIES(msg), 0, mold, VAL_ERR_OBJECT(value));
		}
	} else
		Append_Boot_Str(mold->series, RS_ERRS+1);

	Append_Byte(mold->series, '\n');

	// Form: ** Where: function
	value = &err->where;
	if (VAL_TYPE(value) > REB_NONE) {
		Append_Boot_Str(mold->series, RS_ERRS+2);
		Mold_Value(mold, value, 0);
		Append_Byte(mold->series, '\n');
	}

	// Form: ** Near: location
	value = &err->nearest;
	if (VAL_TYPE(value) > REB_NONE) {
		Append_Boot_Str(mold->series, RS_ERRS+3);
		if (IS_STRING(value)) // special case: source file line number
			Append_String(mold->series, VAL_SERIES(value), 0, VAL_TAIL(value));
		else if (IS_BLOCK(value))
			Mold_Simple_Block(mold, VAL_BLK_DATA(value), 60);
		Append_Byte(mold->series, '\n');
	}
}
Beispiel #2
0
*/	RL_API int RL_Start(REBYTE *bin, REBINT len, REBYTE *script, REBINT script_len, REBCNT flags)
/*
**	Evaluate the default boot function.
**
**	Returns:
**		Zero on success, otherwise indicates an error occurred.
**	Arguments:
**		bin - optional startup code (compressed), can be null
**		len - length of above bin
**		flags - special flags
**	Notes:
**		This function completes the startup sequence by calling
**		the sys/start function.
**
***********************************************************************/
{
	REBVAL *val;
	REBSER *ser;

	REBOL_STATE state;
	const REBVAL *error;

	REBVAL start_result;

	int result;
	REBVAL out;

	if (bin) {
		ser = Decompress(bin, len, -1, FALSE, FALSE);
		if (!ser) return 1;

		val = BLK_SKIP(Sys_Context, SYS_CTX_BOOT_HOST);
		Val_Init_Binary(val, ser);
	}

	if (script && script_len > 4) {
		/* a 4-byte long payload type at the beginning */
		i32 ptype = 0;
		REBYTE *data = script + sizeof(ptype);
		script_len -= sizeof(ptype);

		memcpy(&ptype, script, sizeof(ptype));

		if (ptype == 1) {/* COMPRESSed data */
			ser = Decompress(data, script_len, -1, FALSE, FALSE);
		} else {
			ser = Make_Binary(script_len);
			if (ser == NULL) {
				OS_FREE(script);
				return 1;
			}
			memcpy(BIN_HEAD(ser), data, script_len);
		}
		OS_FREE(script);

		val = BLK_SKIP(Sys_Context, SYS_CTX_BOOT_EMBEDDED);
		Val_Init_Binary(val, ser);
	}

	PUSH_UNHALTABLE_TRAP(&error, &state);

// The first time through the following code 'error' will be NULL, but...
// `raise Error` can longjmp here, so 'error' won't be NULL *if* that happens!

	if (error) {
		// Save error for EXPLAIN and return it
		*Get_System(SYS_STATE, STATE_LAST_ERROR) = *error;

		Print_Value(error, 1024, FALSE);

		// !!! Whether or not the Rebol interpreter just throws and quits
		// in an error case with a bad error code or breaks you into the
		// console to debug the environment should be controlled by
		// a command line option.  Defaulting to returning an error code
		// seems better, because kicking into an interactive session can
		// cause logging systems to hang.  For now we throw instead of
		// just quietly returning a code if the script fails, but add
		// that option!

		// For RE_HALT and all other errors we return the error
		// number.  Error numbers are not set in stone (currently), but
		// are never zero...which is why we can use 0 for success.
		return VAL_ERR_NUM(error);
	}

	if (Do_Sys_Func_Throws(&out, SYS_CTX_FINISH_RL_START, 0)) {
		#if !defined(NDEBUG)
			if (LEGACY(OPTIONS_EXIT_FUNCTIONS_ONLY))
				raise Error_No_Catch_For_Throw(&out);
		#endif

		if (
			IS_NATIVE(&out) && (
				VAL_FUNC_CODE(&out) == VAL_FUNC_CODE(ROOT_QUIT_NATIVE)
				|| VAL_FUNC_CODE(&out) == VAL_FUNC_CODE(ROOT_EXIT_NATIVE)
			)
		) {
			int status;

			CATCH_THROWN(&out, &out);
			status = Exit_Status_From_Value(&out);

			DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

			Shutdown_Core();
			OS_EXIT(status);
			DEAD_END;
		}

		raise Error_No_Catch_For_Throw(&out);
	}

	DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

	// The convention in the API was to return 0 for success.  We use the
	// convention (as for FINISH_INIT_CORE) that any non-UNSET! result from
	// FINISH_RL_START indicates something went wrong.

	if (IS_UNSET(&out))
		result = 0;
	else {
		assert(FALSE); // should not happen (raise an error instead)
		Debug_Fmt("** finish-rl-start returned non-NONE!:");
		Debug_Fmt("%r", &out);
		result = RE_MISC;
	}

	return result;
}
Beispiel #3
0
*/	RL_API int RL_Do_String(int *exit_status, const REBYTE *text, REBCNT flags, RXIARG *result)
/*
**	Load a string and evaluate the resulting block.
**
**	Returns:
**		The datatype of the result if a positive number (or 0 if the
**		type has no representation in the "RXT" API).  An error code
**		if it's a negative number.  Two negative numbers are reserved
**		for non-error conditions: -1 for halting (e.g. Escape), and
**		-2 is reserved for exiting with exit_status set.
**
**	Arguments:
**		text - A null terminated UTF-8 (or ASCII) string to transcode
**			into a block and evaluate.
**		flags - set to zero for now
**		result - value returned from evaluation, if NULL then result
**			will be returned on the top of the stack
**
**	Notes:
**		This API was from before Rebol's open sourcing and had little
**		vetting and few clients.  The one client it did have was the
**		"sample" console code (which wound up being the "only"
**		console code for quite some time).
**
***********************************************************************/
{
	REBSER *code;
	REBVAL out;

	REBOL_STATE state;
	const REBVAL *error;

	// assumes it can only be run at the topmost level where
	// the data stack is completely empty.
	assert(DSP == -1);

	PUSH_UNHALTABLE_TRAP(&error, &state);

// The first time through the following code 'error' will be NULL, but...
// `raise Error` can longjmp here, so 'error' won't be NULL *if* that happens!

	if (error) {
		if (VAL_ERR_NUM(error) == RE_HALT)
			return -1; // !!! Revisit hardcoded #

		// Save error for WHY?
		*Get_System(SYS_STATE, STATE_LAST_ERROR) = *error;

		if (result)
			*result = Value_To_RXI(error);
		else
			DS_PUSH(error);

		return -VAL_ERR_NUM(error);
	}

	code = Scan_Source(text, LEN_BYTES(text));
	PUSH_GUARD_SERIES(code);

	// Bind into lib or user spaces?
	if (flags) {
		// Top words will be added to lib:
		Bind_Values_Set_Forward_Shallow(BLK_HEAD(code), Lib_Context);
		Bind_Values_Deep(BLK_HEAD(code), Lib_Context);
	} else {
		REBCNT len;
		REBVAL vali;
		REBSER *user = VAL_OBJ_FRAME(Get_System(SYS_CONTEXTS, CTX_USER));
		len = user->tail;
		Bind_Values_All_Deep(BLK_HEAD(code), user);
		SET_INTEGER(&vali, len);
		Resolve_Context(user, Lib_Context, &vali, FALSE, 0);
	}

	if (Do_At_Throws(&out, code, 0)) {
		DROP_GUARD_SERIES(code);

		if (
			IS_NATIVE(&out) && (
				VAL_FUNC_CODE(&out) == VAL_FUNC_CODE(ROOT_QUIT_NATIVE)
				|| VAL_FUNC_CODE(&out) == VAL_FUNC_CODE(ROOT_EXIT_NATIVE)
			)
		) {
			CATCH_THROWN(&out, &out);
			DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

			*exit_status = Exit_Status_From_Value(&out);
			return -2; // Revisit hardcoded #
		}

		raise Error_No_Catch_For_Throw(&out);
	}

	DROP_GUARD_SERIES(code);

	DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

	if (result)
		*result = Value_To_RXI(&out);
	else
		DS_PUSH(&out);

	return Reb_To_RXT[VAL_TYPE(&out)];
}
Beispiel #4
0
*/	REBINT Cmp_Value(REBVAL *s, REBVAL *t, REBFLG is_case)
/*
**		Compare two values and return the difference.
**
**		is_case TRUE for case sensitive compare
**
***********************************************************************/
{
	REBDEC	d1, d2;

	if (VAL_TYPE(t) != VAL_TYPE(s) && !(IS_NUMBER(s) && IS_NUMBER(t)))
		return VAL_TYPE(s) - VAL_TYPE(t);

	switch(VAL_TYPE(s)) {

	case REB_INTEGER:
		if (IS_DECIMAL(t)) {
			d1 = (REBDEC)VAL_INT64(s);
			d2 = VAL_DECIMAL(t);
			goto chkDecimal;
		}
		return THE_SIGN(VAL_INT64(s) - VAL_INT64(t));

	case REB_LOGIC:
		return VAL_LOGIC(s) - VAL_LOGIC(t);

	case REB_CHAR:
		if (is_case) return THE_SIGN(VAL_CHAR(s) - VAL_CHAR(t));
		return THE_SIGN((REBINT)(UP_CASE(VAL_CHAR(s)) - UP_CASE(VAL_CHAR(t))));

	case REB_DECIMAL:
	case REB_MONEY:
			d1 = VAL_DECIMAL(s);
		if (IS_INTEGER(t))
			d2 = (REBDEC)VAL_INT64(t);
		else
			d2 = VAL_DECIMAL(t);
chkDecimal:
		if (Eq_Decimal(d1, d2))
			return 0;
		if (d1 < d2)
			return -1;
		return 1;

	case REB_PAIR:
		return Cmp_Pair(s, t);

	case REB_EVENT:
		return Cmp_Event(s, t);

	case REB_GOB:
		return Cmp_Gob(s, t);

	case REB_TUPLE:
		return Cmp_Tuple(s, t);

	case REB_TIME:
		return Cmp_Time(s, t);

	case REB_DATE:
		return Cmp_Date(s, t);

	case REB_BLOCK:
	case REB_PAREN:
	case REB_MAP:
	case REB_PATH:
	case REB_SET_PATH:
	case REB_GET_PATH:
	case REB_LIT_PATH:
		return Cmp_Block(s, t, is_case);

	case REB_STRING:
	case REB_FILE:
	case REB_EMAIL:
	case REB_URL:
	case REB_TAG:
		return Compare_String_Vals(s, t, (REBOOL)!is_case);

	case REB_BITSET:
	case REB_BINARY:
	case REB_IMAGE:
		return Compare_Binary_Vals(s, t);

	case REB_VECTOR:
		return Compare_Vector(s, t);

	case REB_DATATYPE:
		return VAL_DATATYPE(s) - VAL_DATATYPE(t);

	case REB_WORD:
	case REB_SET_WORD:
	case REB_GET_WORD:
	case REB_LIT_WORD:
	case REB_REFINEMENT:
	case REB_ISSUE:
		return Compare_Word(s,t,is_case);

	case REB_ERROR:
		return VAL_ERR_NUM(s) - VAL_ERR_NUM(s);

	case REB_OBJECT:
	case REB_MODULE:
	case REB_PORT:
		return VAL_OBJ_FRAME(s) - VAL_OBJ_FRAME(t);

	case REB_NATIVE:
		return &VAL_FUNC_CODE(s) - &VAL_FUNC_CODE(t);

	case REB_ACTION:
	case REB_COMMAND:
	case REB_OP:
	case REB_FUNCTION:
		return VAL_FUNC_BODY(s) - VAL_FUNC_BODY(t);

	case REB_NONE:
	case REB_UNSET:
	case REB_END:
	default:
		break;

	}
	return 0;
}
Beispiel #5
0
static void Mold_Error(const REBVAL *value, REB_MOLD *mold, REBFLG molded)
{
	ERROR_OBJ *err;
	REBVAL *msg;  // Error message block
	REBSER *frame;

	// Protect against recursion. !!!!

	if (molded) {
		if (VAL_OBJ_FRAME(value) && VAL_ERR_NUM(value) >= RE_NOTE && VAL_ERR_OBJECT(value))
			Mold_Object(value, mold);
		else {
			// Happens if throw or return is molded.
			// make error! 0-3
			Pre_Mold(value, mold);
			Append_Int(mold->series, VAL_ERR_NUM(value));
			End_Mold(mold);
		}
		return;
	}

	if (VAL_ERR_NUM(value) < RE_THROW_MAX) {
		// Though we generally do not make error objects for THROWN() errors,
		// we do make one here for the purposes of molding.
		frame = Make_Error(VAL_ERR_NUM(value), value, 0, 0);
		err = ERR_VALUES(frame);
	}
	else {
		frame = VAL_ERR_OBJECT(value);
		err = VAL_ERR_VALUES(value);
	}

	// Form: ** <type> Error:
	Emit(mold, "** WB", &err->type, RS_ERRS+0);

	// Append: error message ARG1, ARG2, etc.
	msg = Find_Error_Info(err, 0);
	if (msg) {
		if (!IS_BLOCK(msg)) Mold_Value(mold, msg, 0);
		else {
			//start = DSP + 1;
			//Reduce_In_Frame(frame, VAL_BLK_DATA(msg));
			//SERIES_TAIL(DS_Series) = DSP + 1;
			//Form_Block_Series(DS_Series, start, mold, 0);
			Form_Block_Series(VAL_SERIES(msg), 0, mold, frame);
		}
	} else
		Append_Boot_Str(mold->series, RS_ERRS+1);

	Append_Byte(mold->series, '\n');

	// Form: ** Where: function
	value = &err->where;
	if (VAL_TYPE(value) > REB_NONE) {
		Append_Boot_Str(mold->series, RS_ERRS+2);
		Mold_Value(mold, value, 0);
		Append_Byte(mold->series, '\n');
	}

	// Form: ** Near: location
	value = &err->nearest;
	if (VAL_TYPE(value) > REB_NONE) {
		Append_Boot_Str(mold->series, RS_ERRS+3);
		if (IS_STRING(value)) // special case: source file line number
			Append_String(mold->series, VAL_SERIES(value), 0, VAL_TAIL(value));
		else if (IS_BLOCK(value))
			Mold_Simple_Block(mold, VAL_BLK_DATA(value), 60);
		Append_Byte(mold->series, '\n');
	}
}
Beispiel #6
0
*/	void Make_Error_Object(REBVAL *arg, REBVAL *value)
/*
**		Creates an error object from arg and puts it in value.
**		The arg can be a string or an object body block.
**		This function is called by MAKE ERROR!.
**
***********************************************************************/
{
	REBSER *err;		// Error object
	ERROR_OBJ *error;	// Error object values
	REBINT code = 0;

	// Create a new error object from another object, including any non-standard fields:
	if (IS_ERROR(arg) || IS_OBJECT(arg)) {
		err = Merge_Frames(VAL_OBJ_FRAME(ROOT_ERROBJ),
			IS_ERROR(arg) ? VAL_OBJ_FRAME(arg) : VAL_ERR_OBJECT(arg));
		error = ERR_VALUES(err);
//		if (!IS_INTEGER(&error->code)) {
			if (!Find_Error_Info(error, &code)) code = RE_INVALID_ERROR;
			SET_INTEGER(&error->code, code);
//		}
		SET_ERROR(value, VAL_INT32(&error->code), err);
		return;
	}

	// Make a copy of the error object template:
	err = CLONE_OBJECT(VAL_OBJ_FRAME(ROOT_ERROBJ));
	error = ERR_VALUES(err);
	SET_NONE(&error->id);
	SET_ERROR(value, 0, err);

	// If block arg, evaluate object values (checking done later):
	// If user set error code, use it to setup type and id fields.
	if (IS_BLOCK(arg)) {
		DISABLE_GC;
		Do_Bind_Block(err, arg); // GC-OK (disabled)
		ENABLE_GC;
		if (IS_INTEGER(&error->code) && VAL_INT64(&error->code)) {
			Set_Error_Type(error);
		} else {
			if (Find_Error_Info(error, &code)) {
				SET_INTEGER(&error->code, code);
			}
		}
		// The error code is not valid:
		if (IS_NONE(&error->id)) {
			SET_INTEGER(&error->code, RE_INVALID_ERROR);
			Set_Error_Type(error);
		}
		if (VAL_INT64(&error->code) < 100 || VAL_INT64(&error->code) > 1000)
			Trap_Arg(arg);
	}

	// If string arg, setup other fields
	else if (IS_STRING(arg)) {
		SET_INTEGER(&error->code, RE_USER); // user error
		Set_String(&error->arg1, Copy_Series_Value(arg));
		Set_Error_Type(error);
	}

// No longer allowed:
//	else if (IS_INTEGER(arg)) {
//		error->code = *arg;
//		Set_Error_Type(error);
//	}
	else
		Trap_Arg(arg);

	if (!(VAL_ERR_NUM(value) = VAL_INT32(&error->code))) {
		Trap_Arg(arg);
	}
}
Beispiel #7
0
*/	RL_API void RL_Print_TOS(REBCNT flags, REBYTE *marker)
/*
**	Print top REBOL stack value to the console. (pending changes)
**
**	Returns:
**		Nothing
**	Arguments:
**		flags - special flags (set to zero at this time).
**		marker - placed at beginning of line to indicate output.
**	Notes:
**		This function is used for the main console evaluation
**		input loop to print the results of evaluation from stack.
**		The REBOL data stack is an abstract structure that can
**		change between releases. This function allows the host
**		to print the result of processed functions.
**		Note that what is printed is actually TOS+1.
**		Marker is usually "==" to show output.
**		The system/options/result-types determine which values
**		are automatically printed.
**
***********************************************************************/
{
	REBINT dsp = DSP;
	REBVAL *top = DS_VALUE(dsp+1);
	REBOL_STATE state;
	REBVAL *types;

	if (dsp != 0) Debug_Fmt(Str_Stack_Misaligned, dsp);

	PUSH_STATE(state, Saved_State);
	if (SET_JUMP(state)) {
		POP_STATE(state, Saved_State);
		Catch_Error(DS_NEXT); // Stores error value here
		Out_Value(DS_NEXT, 0, FALSE, 0); // error
		DSP = 0;
		return;
	}
	SET_STATE(state, Saved_State);

	if (!IS_UNSET(top)) {
		if (!IS_ERROR(top)) {
			types = Get_System(SYS_OPTIONS, OPTIONS_RESULT_TYPES);
			if (IS_TYPESET(types) && TYPE_CHECK(types, VAL_TYPE(top))) {
				if (marker) Out_Str(marker, 0);
				Out_Value(top, 500, TRUE, 1); // limit, molded
			}
//			else {
//				Out_Str(Get_Type_Name(top), 1);
//			}
		} else {
			if (VAL_ERR_NUM(top) != RE_HALT) {
				Out_Value(top, 640, FALSE, 0); // error FORMed
//				if (VAL_ERR_NUM(top) > RE_THROW_MAX) {
//					Out_Str("** Note: use WHY? for more about this error", 1);
//				}
			}
		}
	}

	POP_STATE(state, Saved_State);
	DSP = 0;
}
Beispiel #8
0
//
//  Cmp_Value: C
// 
// Compare two values and return the difference.
// 
// is_case TRUE for case sensitive compare
//
REBINT Cmp_Value(const RELVAL *s, const RELVAL *t, REBOOL is_case)
{
    REBDEC  d1, d2;

    if (VAL_TYPE(t) != VAL_TYPE(s) && !(ANY_NUMBER(s) && ANY_NUMBER(t)))
        return VAL_TYPE(s) - VAL_TYPE(t);

    assert(NOT_END(s) && NOT_END(t));

    switch(VAL_TYPE(s)) {

    case REB_INTEGER:
        if (IS_DECIMAL(t)) {
            d1 = (REBDEC)VAL_INT64(s);
            d2 = VAL_DECIMAL(t);
            goto chkDecimal;
        }
        return THE_SIGN(VAL_INT64(s) - VAL_INT64(t));

    case REB_LOGIC:
        return VAL_LOGIC(s) - VAL_LOGIC(t);

    case REB_CHAR:
        if (is_case) return THE_SIGN(VAL_CHAR(s) - VAL_CHAR(t));
        return THE_SIGN((REBINT)(UP_CASE(VAL_CHAR(s)) - UP_CASE(VAL_CHAR(t))));

    case REB_PERCENT:
    case REB_DECIMAL:
    case REB_MONEY:
        if (IS_MONEY(s))
            d1 = deci_to_decimal(VAL_MONEY_AMOUNT(s));
        else
            d1 = VAL_DECIMAL(s);
        if (IS_INTEGER(t))
            d2 = cast(REBDEC, VAL_INT64(t));
        else if (IS_MONEY(t))
            d2 = deci_to_decimal(VAL_MONEY_AMOUNT(t));
        else
            d2 = VAL_DECIMAL(t);
chkDecimal:
        if (Eq_Decimal(d1, d2))
            return 0;
        if (d1 < d2)
            return -1;
        return 1;

    case REB_PAIR:
        return Cmp_Pair(s, t);

    case REB_EVENT:
        return Cmp_Event(s, t);

    case REB_GOB:
        return Cmp_Gob(s, t);

    case REB_TUPLE:
        return Cmp_Tuple(s, t);

    case REB_TIME:
        return Cmp_Time(s, t);

    case REB_DATE:
        return Cmp_Date(s, t);

    case REB_BLOCK:
    case REB_GROUP:
    case REB_MAP:
    case REB_PATH:
    case REB_SET_PATH:
    case REB_GET_PATH:
    case REB_LIT_PATH:
        return Cmp_Array(s, t, is_case);

    case REB_STRING:
    case REB_FILE:
    case REB_EMAIL:
    case REB_URL:
    case REB_TAG:
        return Compare_String_Vals(s, t, NOT(is_case));

    case REB_BITSET:
    case REB_BINARY:
    case REB_IMAGE:
        return Compare_Binary_Vals(s, t);

    case REB_VECTOR:
        return Compare_Vector(s, t);

    case REB_DATATYPE:
        return VAL_TYPE_KIND(s) - VAL_TYPE_KIND(t);

    case REB_WORD:
    case REB_SET_WORD:
    case REB_GET_WORD:
    case REB_LIT_WORD:
    case REB_REFINEMENT:
    case REB_ISSUE:
        return Compare_Word(s,t,is_case);

    case REB_ERROR:
        return VAL_ERR_NUM(s) - VAL_ERR_NUM(t);

    case REB_OBJECT:
    case REB_MODULE:
    case REB_PORT:
        return VAL_CONTEXT(s) - VAL_CONTEXT(t);

    case REB_FUNCTION:
        return VAL_FUNC_PARAMLIST(s) - VAL_FUNC_PARAMLIST(t);

    case REB_LIBRARY:
        return VAL_LIBRARY(s) - VAL_LIBRARY(t);

    case REB_STRUCT:
        return Cmp_Struct(s, t);

    case REB_BLANK:
    case REB_MAX_VOID:
    default:
        break;

    }
    return 0;
}