diff options
| author | dkf <donal.k.fellows@manchester.ac.uk> | 2006-11-24 15:34:23 (GMT) | 
|---|---|---|
| committer | dkf <donal.k.fellows@manchester.ac.uk> | 2006-11-24 15:34:23 (GMT) | 
| commit | f8b1db93f84f1296cdc14b38c7de3c2538976f45 (patch) | |
| tree | b688d0d1da7aa9a4ffc921a3733736c2950f59b5 /generic/tclCompCmds.c | |
| parent | bc1d21f318d9ce36a03d17c9c54b6be84cb8291a (diff) | |
| download | tcl-f8b1db93f84f1296cdc14b38c7de3c2538976f45.zip tcl-f8b1db93f84f1296cdc14b38c7de3c2538976f45.tar.gz tcl-f8b1db93f84f1296cdc14b38c7de3c2538976f45.tar.bz2  | |
Added some more implementations
Diffstat (limited to 'generic/tclCompCmds.c')
| -rw-r--r-- | generic/tclCompCmds.c | 277 | 
1 files changed, 274 insertions, 3 deletions
diff --git a/generic/tclCompCmds.c b/generic/tclCompCmds.c index 09e70e2..152597b 100644 --- a/generic/tclCompCmds.c +++ b/generic/tclCompCmds.c @@ -12,7 +12,7 @@   * See the file "license.terms" for information on usage and redistribution   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.   * - * RCS: @(#) $Id: tclCompCmds.c,v 1.90 2006/11/24 15:06:25 dgp Exp $ + * RCS: @(#) $Id: tclCompCmds.c,v 1.91 2006/11/24 15:34:23 dkf Exp $   */  #include "tclInt.h" @@ -4856,12 +4856,132 @@ TclLshiftOpCmd(      int objc,      Tcl_Obj *const objv[])  { +    ClientData ptr1, ptr2; +    int invalid, shift, type1, type2, idx; +    const char *description; +    long l1; +      if (objc != 3) {  	Tcl_WrongNumArgs(interp, 1, objv, "value value");  	return TCL_ERROR;      } -    Tcl_AppendResult(interp, "not yet implemented", NULL); +    if ((TclGetNumberFromObj(NULL, objv[1], &ptr1, &type1) != TCL_OK) +	    || (type1 == TCL_NUMBER_DOUBLE) || (type1 == TCL_NUMBER_NAN)) { +	idx = 1; +	goto illegalOperand; +    } +    if ((TclGetNumberFromObj(NULL, objv[2], &ptr2, &type2) != TCL_OK) +	    || (type2 == TCL_NUMBER_DOUBLE) || (type2 == TCL_NUMBER_NAN)) { +	idx = 2; +	goto illegalOperand; +    } + +    /* reject negative shift argument */ +    switch (type2) { +    case TCL_NUMBER_LONG: +	invalid = (*((const long *)ptr2) < (long)0); +	break; +#ifndef NO_WIDE_TYPE +    case TCL_NUMBER_WIDE: +	invalid = (*((const Tcl_WideInt *)ptr2) < (Tcl_WideInt)0); +	break; +#endif +    case TCL_NUMBER_BIG: +	/* TODO: const correctness ? */ +	invalid = (mp_cmp_d((mp_int *)ptr2, 0) == MP_LT); +	break; +    default: +	/* Unused, here to silence compiler warning */ +	invalid = 0; +    } +    if (invalid) { +	Tcl_SetObjResult(interp, +		Tcl_NewStringObj("negative shift argument", -1)); +	return TCL_ERROR; +    } + +    /* Zero shifted any number of bits is still zero */ +    if ((type1 == TCL_NUMBER_LONG) && (*((const long *)ptr1) == (long)0)) { +	Tcl_SetObjResult(interp, Tcl_NewIntObj(0)); +	return TCL_OK; +    } + +    /* Large left shifts create integer overflow */ +    if (Tcl_GetIntFromObj(NULL, objv[2], &shift) != TCL_OK) { +	/* +	 * Technically, we could hold the value (1 << (INT_MAX+1)) in an +	 * mp_int, but since we're using mp_mul_2d() to do the work, and it +	 * takes only an int argument, that's a good place to draw the line. +	 */ + +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +		"integer value too large to represent", -1)); +	return TCL_ERROR; +    } + +    /* Handle shifts within the native long range */ +    if ((type1 == TCL_NUMBER_LONG) && ((size_t)shift < CHAR_BIT*sizeof(long)) +	    && (l1 = *((CONST long *)ptr1)) && +	    !(((l1>0) ? l1 : ~l1) & -(1L<<(CHAR_BIT*sizeof(long)-1-shift)))) { +	Tcl_SetObjResult(interp, Tcl_NewLongObj(l1<<shift)); +	return TCL_OK; +    } + +    /* Handle shifts within the native wide range */ +    if ((type1 != TCL_NUMBER_BIG) +	    && ((size_t)shift < CHAR_BIT*sizeof(Tcl_WideInt))) { +	Tcl_WideInt w; + +	Tcl_GetWideIntFromObj(NULL, objv[1], &w); +	if (!(((w>0) ? w : ~w) & -(((Tcl_WideInt)1) +		<< (CHAR_BIT*sizeof(Tcl_WideInt)-1-shift)))) { +	    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(w<<shift)); +	    return TCL_OK; +	} +    } + +    { +	mp_int big, bigResult; + +	if (Tcl_IsShared(objv[1])) { +	    Tcl_GetBignumFromObj(NULL, objv[1], &big); +	} else { +	    Tcl_GetBignumAndClearObj(NULL, objv[1], &big); +	} + +	mp_init(&bigResult); +	mp_mul_2d(&big, shift, &bigResult); +	mp_clear(&big); + +	if (!Tcl_IsShared(objv[1])) { +	    Tcl_SetBignumObj(objv[1], &bigResult); +	    Tcl_SetObjResult(interp, objv[1]); +	} else { +	    Tcl_SetObjResult(interp, Tcl_NewBignumObj(&bigResult)); +	} +    } +    return TCL_OK; + +  illegalOperand: +    if (TclGetNumberFromObj(NULL, objv[idx], &ptr1, &type1) != TCL_OK) { +	int numBytes; +	const char *bytes = Tcl_GetStringFromObj(objv[idx], &numBytes); +	if (numBytes == 0) { +	    description = "empty string"; +	} else if (TclCheckBadOctal(NULL, bytes)) { +	    description = "invalid octal number"; +	} else { +	    description = "non-numeric string"; +	} +    } else if (type1 == TCL_NUMBER_NAN) { +	description = "non-numeric floating-point value"; +    } else { +	description = "floating-point value"; +    } + +    Tcl_SetObjResult(interp, +	    Tcl_ObjPrintf("can't use %s as operand of \"<<\"", description));      return TCL_ERROR;  } @@ -4891,12 +5011,163 @@ TclRshiftOpCmd(      int objc,      Tcl_Obj *const objv[])  { +    ClientData ptr1, ptr2; +    int invalid, shift, type1, type2, idx; +    const char *description; +    long l1; +      if (objc != 3) {  	Tcl_WrongNumArgs(interp, 1, objv, "value value");  	return TCL_ERROR;      } -    Tcl_AppendResult(interp, "not yet implemented", NULL); +    if ((TclGetNumberFromObj(NULL, objv[1], &ptr1, &type1) != TCL_OK) +	    || (type1 == TCL_NUMBER_DOUBLE) || (type1 == TCL_NUMBER_NAN)) { +	idx = 1; +	goto illegalOperand; +    } +    if ((TclGetNumberFromObj(NULL, objv[2], &ptr2, &type2) != TCL_OK) +	    || (type2 == TCL_NUMBER_DOUBLE) || (type2 == TCL_NUMBER_NAN)) { +	idx = 2; +	goto illegalOperand; +    } + +    /* reject negative shift argument */ +    switch (type2) { +    case TCL_NUMBER_LONG: +	invalid = (*((const long *)ptr2) < (long)0); +	break; +#ifndef NO_WIDE_TYPE +    case TCL_NUMBER_WIDE: +	invalid = (*((const Tcl_WideInt *)ptr2) < (Tcl_WideInt)0); +	break; +#endif +    case TCL_NUMBER_BIG: +	/* TODO: const correctness ? */ +	invalid = (mp_cmp_d((mp_int *)ptr2, 0) == MP_LT); +	break; +    default: +	/* Unused, here to silence compiler warning */ +	invalid = 0; +    } +    if (invalid) { +	Tcl_SetObjResult(interp, +		Tcl_NewStringObj("negative shift argument", -1)); +	return TCL_ERROR; +    } + +    /* Zero shifted any number of bits is still zero */ +    if ((type1 == TCL_NUMBER_LONG) && (*((const long *)ptr1) == (long)0)) { +	Tcl_SetObjResult(interp, Tcl_NewIntObj(0)); +	return TCL_OK; +    } + +    /* Quickly force large right shifts to 0 or -1 */ +    if ((type2 != TCL_NUMBER_LONG) +	    || (*((const long *)ptr2) > INT_MAX)) { +	/* +	 * Again, technically, the value to be shifted could be an mp_int so +	 * huge that a right shift by (INT_MAX+1) bits could not take us to +	 * the result of 0 or -1, but since we're using mp_div_2d to do the +	 * work, and it takes only an int argument, we draw the line there. +	 */ + +	int zero; + +	switch (type1) { +	case TCL_NUMBER_LONG: +	    zero = (*((const long *)ptr1) > (long)0); +	    break; +#ifndef NO_WIDE_TYPE +	case TCL_NUMBER_WIDE: +	    zero = (*((const Tcl_WideInt *)ptr1) > (Tcl_WideInt)0); +	    break; +#endif +	case TCL_NUMBER_BIG: +	    /* TODO: const correctness ? */ +	    zero = (mp_cmp_d((mp_int *)ptr1, 0) == MP_GT); +	    break; +	default: +	    /* Unused, here to silence compiler warning. */ +	    zero = 0; +	} +	Tcl_SetObjResult(interp, Tcl_NewIntObj(zero ? 0 : -1)); +	return TCL_OK; +    } + +    shift = (int)(*((const long *)ptr2)); +    /* Handle shifts within the native long range */ +    if (type1 == TCL_NUMBER_LONG) { +	l1 = *((const long *)ptr1); +	if ((size_t)shift >= CHAR_BIT*sizeof(long)) { +	    Tcl_SetObjResult(interp, Tcl_NewIntObj(l1 >= (long)0 ? 0 : -1)); +	} else { +	    Tcl_SetObjResult(interp, Tcl_NewIntObj(l1 >> shift)); +	} +	return TCL_OK; +    } + +#ifndef NO_WIDE_TYPE +    /* Handle shifts within the native wide range */ +    if (type1 == TCL_NUMBER_WIDE) { +	Tcl_WideInt w = *((const Tcl_WideInt *)ptr1); +	if ((size_t)shift >= CHAR_BIT*sizeof(Tcl_WideInt)) { +	    Tcl_SetObjResult(interp, +		    Tcl_NewIntObj(w >= (Tcl_WideInt)0 ? 0 : -1)); +	} else { +	    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(w >> shift)); +	} +	return TCL_OK; +    } +#endif + +    { +	mp_int big, bigResult, bigRemainder; + +	if (Tcl_IsShared(objv[1])) { +	    Tcl_GetBignumFromObj(NULL, objv[1], &big); +	} else { +	    Tcl_GetBignumAndClearObj(NULL, objv[1], &big); +	} + +	mp_init(&bigResult); +	mp_init(&bigRemainder); +	mp_div_2d(&big, shift, &bigResult, &bigRemainder); +	if (mp_cmp_d(&bigRemainder, 0) == MP_LT) { +	    /* Convert to Tcl's integer division rules */ +	    mp_sub_d(&bigResult, 1, &bigResult); +	} +	mp_clear(&bigRemainder); +	mp_clear(&big); + +	if (!Tcl_IsShared(objv[1])) { +	    Tcl_SetBignumObj(objv[1], &bigResult); +	    Tcl_SetObjResult(interp, objv[1]); +	} else { +	    Tcl_SetObjResult(interp, Tcl_NewBignumObj(&bigResult)); +	} +    } +    return TCL_OK; + +  illegalOperand: +    if (TclGetNumberFromObj(NULL, objv[idx], &ptr1, &type1) != TCL_OK) { +	int numBytes; +	const char *bytes = Tcl_GetStringFromObj(objv[idx], &numBytes); +	if (numBytes == 0) { +	    description = "empty string"; +	} else if (TclCheckBadOctal(NULL, bytes)) { +	    description = "invalid octal number"; +	} else { +	    description = "non-numeric string"; +	} +    } else if (type1 == TCL_NUMBER_NAN) { +	description = "non-numeric floating-point value"; +    } else { +	description = "floating-point value"; +    } + +    Tcl_SetObjResult(interp, +	    Tcl_ObjPrintf("can't use %s as operand of \">>\"", description));      return TCL_ERROR;  }  | 
