diff dmdscript_tango/dnumber.d @ 0:55c2951c07be

initial, files origin, premoved tree
author saaadel
date Sun, 24 Jan 2010 12:34:47 +0200
parents
children 8363a4bf6a8f
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dmdscript_tango/dnumber.d	Sun Jan 24 12:34:47 2010 +0200
@@ -0,0 +1,666 @@
+
+/* Digital Mars DMDScript source code.
+ * Copyright (c) 2000-2002 by Chromium Communications
+ * D version Copyright (c) 2004-2006 by Digital Mars
+ * All Rights Reserved
+ * written by Walter Bright
+ * www.digitalmars.com
+ * Use at your own risk. There is no warranty, express or implied.
+ * License for redistribution is by the GNU General Public License in gpl.txt.
+ *
+ * A binary, non-exclusive license for commercial use can be
+ * purchased from www.digitalmars.com/dscript/buy.html.
+ *
+ * DMDScript is implemented in the D Programming Language,
+ * www.digitalmars.com/d/
+ *
+ * For a C++ implementation of DMDScript, including COM support,
+ * see www.digitalmars.com/dscript/cppscript.html.
+ */
+
+
+module dmdscript.dnumber;
+
+import std.math;
+import std.c.stdlib;
+
+import dmdscript.script;
+import dmdscript.dobject;
+import dmdscript.dfunction;
+import dmdscript.value;
+import dmdscript.threadcontext;
+import dmdscript.text;
+import dmdscript.property;
+import dmdscript.errmsgs;
+import dmdscript.dnative;
+
+/* ===================== Dnumber_constructor ==================== */
+
+class Dnumber_constructor : Dfunction
+{
+    this(ThreadContext *tc)
+    {
+	super(1, tc.Dfunction_prototype);
+	uint attributes = DontEnum | DontDelete | ReadOnly;
+
+	name = TEXT_Number;
+	Put(TEXT_MAX_VALUE, d_number.max, attributes);
+	Put(TEXT_MIN_VALUE, d_number.min, attributes);
+	Put(TEXT_NaN, d_number.nan, attributes);
+	Put(TEXT_NEGATIVE_INFINITY, -d_number.infinity, attributes);
+	Put(TEXT_POSITIVE_INFINITY, d_number.infinity, attributes);
+    }
+
+    void* Construct(CallContext *cc, Value *ret, Value[] arglist)
+    {
+	// ECMA 15.7.2
+	d_number n;
+	Dobject o;
+
+	n = (arglist.length) ? arglist[0].toNumber() : 0;
+	o = new Dnumber(n);
+	ret.putVobject(o);
+	return null;
+    }
+
+    void* Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
+    {
+	// ECMA 15.7.1
+	d_number n;
+
+	n = (arglist.length) ? arglist[0].toNumber() : 0;
+	ret.putVnumber(n);
+	return null;
+    }
+}
+
+
+/* ===================== Dnumber_prototype_toString =============== */
+
+void* Dnumber_prototype_toString(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
+{
+    // ECMA v3 15.7.4.2
+    d_string s;
+
+    // othis must be a Number
+    if (!othis.isClass(TEXT_Number))
+    {
+	ret.putVundefined();
+	ErrInfo errinfo;
+	return Dobject.RuntimeError(&errinfo,
+		errmsgtbl[ERR_FUNCTION_WANTS_NUMBER],
+		TEXT_toString,
+		othis.classname);
+    }
+    else
+    {	Value* v;
+
+	v = &(cast(Dnumber)othis).value;
+
+	if (arglist.length)
+	{
+	    d_number radix;
+
+	    radix = arglist[0].toNumber();
+	    if (radix == 10.0 || arglist[0].isUndefined())
+		s = v.toString();
+	    else
+	    {
+		int r;
+
+		r = cast(int) radix;
+		// radix must be an integer 2..36
+		if (r == radix && r >= 2 && r <= 36)
+		    s = v.toString(r);
+		else
+		    s = v.toString();
+	    }
+	}
+	else
+	    s = v.toString();
+	ret.putVstring(s);
+    }
+    return null;
+}
+
+/* ===================== Dnumber_prototype_toLocaleString =============== */
+
+void* Dnumber_prototype_toLocaleString(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
+{
+    // ECMA v3 15.7.4.3
+    d_string s;
+
+    // othis must be a Number
+    if (!othis.isClass(TEXT_Number))
+    {
+	ret.putVundefined();
+	ErrInfo errinfo;
+	return Dobject.RuntimeError(&errinfo,
+		errmsgtbl[ERR_FUNCTION_WANTS_NUMBER],
+		TEXT_toLocaleString,
+		othis.classname);
+    }
+    else
+    {	Value* v;
+
+	v = &(cast(Dnumber)othis).value;
+
+	s = v.toLocaleString();
+	ret.putVstring(s);
+    }
+    return null;
+}
+
+/* ===================== Dnumber_prototype_valueOf =============== */
+
+void* Dnumber_prototype_valueOf(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
+{
+    // othis must be a Number
+    if (!othis.isClass(TEXT_Number))
+    {
+	ret.putVundefined();
+	ErrInfo errinfo;
+	return Dobject.RuntimeError(&errinfo,
+		errmsgtbl[ERR_FUNCTION_WANTS_NUMBER],
+		TEXT_valueOf,
+		othis.classname);
+    }
+    else
+    {
+	Value* v;
+
+	v = &(cast(Dnumber)othis).value;
+	Value.copy(ret, v);
+    }
+    return null;
+}
+
+/* ===================== Formatting Support =============== */
+
+const int FIXED_DIGITS = 20;	// ECMA says >= 20
+
+
+// power of tens array, indexed by power
+
+static d_number tens[FIXED_DIGITS+1] =
+[
+    1,   1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+    1e10,1e11,1e12,1e13,1e14,1e15,1e16,1e17,1e18,1e19,
+    1e20,
+];
+
+/************************************************
+ * Let e and n be integers such that
+ * 10**f <= n < 10**(f+1) and for which the exact
+ * mathematical value of n * 10**(e-f) - x is as close
+ * to zero as possible. If there are two such sets of
+ * e and n, pick the e and n for which n * 10**(e-f)
+ * is larger.
+ */
+
+number_t deconstruct_real(d_number x, int f, out int pe)
+{
+    number_t n;
+    int e;
+    int i;
+
+    e = cast(int) log10(x);
+    i = e - f;
+    if (i >= 0 && i < tens.length)
+	// table lookup for speed & accuracy
+	n = cast(number_t)(x / tens[i] + 0.5);
+    else
+	n = cast(number_t)(x / std.math.pow(cast(real)10.0, i) + 0.5);
+
+    pe = e;
+    return n;
+}
+
+/* ===================== Dnumber_prototype_toFixed =============== */
+
+void* Dnumber_prototype_toFixed(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
+{
+    // ECMA v3 15.7.4.5
+    Value* v;
+    d_number x;
+    d_number fractionDigits;
+    d_string result;
+    int dup;
+
+    v = &arglist[0];
+    fractionDigits = (arglist.length) ? v.toInteger() : 0;
+    if (fractionDigits < 0 || fractionDigits > FIXED_DIGITS)
+    {
+	ErrInfo errinfo;
+
+	ret.putVundefined();
+	return Dobject.RangeError(&errinfo, ERR_VALUE_OUT_OF_RANGE,
+		TEXT_toFixed, "fractionDigits");
+    }
+    v = &othis.value;
+    x = v.toNumber();
+    if (isnan(x))
+    {
+	result = TEXT_NaN;		// return "NaN"
+    }
+    else
+    {	int sign;
+	tchar[] m;
+
+	sign = 0;
+	if (x < 0)
+	{
+	    sign = 1;
+	    x = -x;
+	}
+	if (x >= 10.0e+21)		// exponent must be FIXED_DIGITS+1
+	{
+	    Value vn;
+	    vn.putVnumber(x);
+	    m = vn.toString();
+	    dup = 0;
+	}
+	else
+	{
+	    number_t n;
+	    tchar buffer[32 + 1];
+	    d_number tenf;
+	    int f;
+
+	    f = cast(int) fractionDigits;
+	    tenf = tens[f];		// tenf = 10**f
+
+	    // Compute n which gives |(n / tenf) - x| is the smallest
+	    // value. If there are two such n's, pick the larger.
+	    n = cast(number_t)(x * tenf + 0.5);		// round up & chop
+
+	    if (n == 0)
+	    {	m = "0";
+		dup = 0;
+	    }
+	    else
+	    {
+		// n still doesn't give 20 digits, only 19
+		m = std.string.sformat(buffer, cast(ulong)n);
+		dup = 1;
+	    }
+	    if (f != 0)
+	    {	int i;
+		int k;
+
+		k = m.length;
+		if (k <= f)
+		{   tchar* s;
+		    int nzeros;
+
+		    s = cast(tchar*)alloca((f + 1) * tchar.sizeof);
+		    assert(s);
+		    nzeros = f + 1 - k;
+		    s[0 .. nzeros] = '0';
+		    s[nzeros .. f + 1] = m[0 .. k];
+
+		    m = s[0 .. f + 1];
+		    k = f + 1;
+		}
+
+		// result = "-" + m[0 .. k-f] + "." + m[k-f .. k];
+		result = new tchar[sign + k + 1];
+		if (sign)
+		    result[0] = '-';
+		i = k - f;
+		result[sign .. sign + i] = m[0 .. i];
+		result[sign + i] = '.';
+		result[sign + i + 1 .. sign + k + 1] = m[i .. k];
+		goto Ldone;
+	    }
+	}
+	if (sign)
+	    result = TEXT_dash ~ m;
+	else if (dup)
+	    result = m.dup;
+	else
+	    result = m;
+    }
+
+Ldone:
+    ret.putVstring(result);
+    return null;
+}
+
+/* ===================== Dnumber_prototype_toExponential =============== */
+
+void* Dnumber_prototype_toExponential(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
+{
+    // ECMA v3 15.7.4.6
+    Value* varg;
+    Value* v;
+    d_number x;
+    d_number fractionDigits;
+    d_string result;
+
+    varg = &arglist[0];
+    fractionDigits = (arglist.length) ? varg.toInteger() : 0;
+
+    v = &othis.value;
+    x = v.toNumber();
+    if (isnan(x))
+    {
+	result = TEXT_NaN;		// return "NaN"
+    }
+    else
+    {	int sign;
+
+	sign = 0;
+	if (x < 0)
+	{
+	    sign = 1;
+	    x = -x;
+	}
+	if (std.math.isinf(x))
+	{
+	    result = sign ? TEXT_negInfinity : TEXT_Infinity;
+	}
+	else
+	{   int f;
+	    number_t n;
+	    int e;
+	    tchar[] m;
+	    int i;
+	    tchar buffer[32 + 1];
+
+	    if (fractionDigits < 0 || fractionDigits > FIXED_DIGITS)
+            {
+		ErrInfo errinfo;
+
+		ret.putVundefined();
+		return Dobject.RangeError(&errinfo,
+			ERR_VALUE_OUT_OF_RANGE,
+			TEXT_toExponential,
+			"fractionDigits");
+            }
+
+	    f = cast(int) fractionDigits;
+	    if (x == 0)
+	    {	tchar* s;
+
+		s = cast(tchar*)alloca((f + 1) * tchar.sizeof);
+		assert(s);
+		m = s[0 .. f + 1];
+		m[0 .. f + 1] = '0';
+		e = 0;
+	    }
+	    else
+	    {
+		if (arglist.length && !varg.isUndefined())
+		{
+		    /* Step 12
+		     * Let e and n be integers such that
+		     * 10**f <= n < 10**(f+1) and for which the exact
+		     * mathematical value of n * 10**(e-f) - x is as close
+		     * to zero as possible. If there are two such sets of
+		     * e and n, pick the e and n for which n * 10**(e-f)
+		     * is larger.
+		     * [Note: this is the same as Step 15 in toPrecision()
+		     *  with f = p - 1]
+		     */
+		    n = deconstruct_real(x, f, e);
+		}
+		else
+		{
+		    /* Step 19
+		     * Let e, n, and f be integers such that f >= 0,
+		     * 10**f <= n < 10**(f+1), the number value for
+		     * n * 10**(e-f) is x, and f is as small as possible.
+		     * Note that the decimal representation of n has f+1
+		     * digits, n is not divisible by 10, and the least
+		     * significant digit of n is not necessarilly uniquely
+		     * determined by these criteria.
+		     */
+		    /* Implement by trying maximum digits, and then
+		     * lopping off trailing 0's.
+		     */
+		    f = 19;		// should use FIXED_DIGITS
+		    n = deconstruct_real(x, f, e);
+
+		    // Lop off trailing 0's
+		    assert(n);
+		    while ((n % 10) == 0)
+		    {
+			n /= 10;
+			f--;
+			assert(f >= 0);
+		    }
+		}
+		// n still doesn't give 20 digits, only 19
+		m = std.string.sformat(buffer, cast(ulong)n);
+	    }
+	    if (f)
+	    {	tchar* s;
+
+		// m = m[0] + "." + m[1 .. f+1];
+		s = cast(tchar*)alloca((f + 2) * tchar.sizeof);
+		assert(s);
+		s[0] = m[0];
+		s[1] = '.';
+		s[2 .. f + 2] = m[1 .. f + 1];
+		m = s[0 .. f + 2];
+	    }
+
+	    // result = sign + m + "e" + c + e;
+	    tchar[] c = (e >= 0) ? "+" : "";
+
+	    result = std.string.format("%s%se%s%d", sign ? "-" : "", m, c, e);
+	}
+    }
+
+    ret.putVstring(result);
+    return null;
+}
+
+/* ===================== Dnumber_prototype_toPrecision =============== */
+
+void* Dnumber_prototype_toPrecision(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
+{
+    // ECMA v3 15.7.4.7
+    Value* varg;
+    Value* v;
+    d_number x;
+    d_number precision;
+    d_string result;
+
+    v = &othis.value;
+    x = v.toNumber();
+
+    varg = (arglist.length == 0) ? &vundefined : &arglist[0];
+
+    if (arglist.length == 0 || varg.isUndefined())
+    {
+	Value vn;
+
+	vn.putVnumber(x);
+	result = vn.toString();
+    }
+    else
+    {
+	if (isnan(x))
+	    result = TEXT_NaN;
+	else
+	{   int sign;
+	    int e;
+	    int p;
+	    int i;
+	    tchar[] m;
+	    number_t n;
+	    tchar buffer[32 + 1];
+
+	    sign = 0;
+	    if (x < 0)
+	    {
+		sign = 1;
+		x = -x;
+	    }
+
+	    if (std.math.isinf(x))
+	    {
+		result = sign ? TEXT_negInfinity : TEXT_Infinity;
+		goto Ldone;
+	    }
+
+	    precision = varg.toInteger();
+	    if (precision < 1 || precision > 21)
+            {
+		ErrInfo errinfo;
+
+		ret.putVundefined();
+		return Dobject.RangeError(&errinfo,
+			ERR_VALUE_OUT_OF_RANGE,
+			TEXT_toPrecision,
+			"precision");
+            }
+
+	    p = cast(int) precision;
+	    if (x != 0)
+	    {
+		/* Step 15
+		 * Let e and n be integers such that 10**(p-1) <= n < 10**p
+		 * and for which the exact mathematical value of n * 10**(e-p+1) - x
+		 * is as close to zero as possible. If there are two such sets
+		 * of e and n, pick the e and n for which n * 10**(e-p+1) is larger.
+		 */
+		n = deconstruct_real(x, p - 1, e);
+
+		// n still doesn't give 20 digits, only 19
+		m = std.string.sformat(buffer, cast(ulong)n);
+
+		if (e < -6 || e >= p)
+		{
+		    // result = sign + m[0] + "." + m[1 .. p] + "e" + c + e;
+		    tchar[] c = (e >= 0) ? "+" : "";
+		    result = std.string.format("%s%s.%se%s%d",
+			(sign ? "-" : ""), m[0], m[1 .. length], c, e);
+		    goto Ldone;
+		}
+	    }
+	    else
+	    {
+		// Step 12
+		// m = array[p] of '0'
+		tchar* s;
+		s = cast(tchar*)alloca(p * tchar.sizeof);
+		assert(s);
+		m = s[0 .. p];
+		m[] = '0';
+
+		e = 0;
+	    }
+	    if (e != p - 1)
+	    {	tchar* s;
+
+		if (e >= 0)
+		{
+		    // m = m[0 .. e+1] + "." + m[e+1 .. p];
+
+		    s = cast(tchar*)alloca((p + 1) * tchar.sizeof);
+		    assert(s);
+		    i = e + 1;
+		    s[0 .. i] = m[0 .. i];
+		    s[i] = '.';
+		    s[i + 1 .. p + 1] = m[i .. p];
+		    m = s[0 .. p + 1];
+		}
+		else
+		{
+		    // m = "0." + (-(e+1) occurrences of the character '0') + m;
+		    int imax = 2 + -(e + 1);
+
+		    s = cast(tchar*)alloca((imax + p) * tchar.sizeof);
+		    assert(s);
+		    s[0] = '0';
+		    s[1] = '.';
+		    s[2 .. imax] = '0';
+		    s[imax .. imax + p] = m[0 .. p];
+		    m = s[0 .. imax + p];
+		}
+	    }
+	    if (sign)
+		result = TEXT_dash ~ m;
+	    else
+		result = m.dup;
+	}
+    }
+
+Ldone:
+    ret.putVstring(result);
+    return null;
+}
+
+/* ===================== Dnumber_prototype ==================== */
+
+class Dnumber_prototype : Dnumber
+{
+    this(ThreadContext *tc)
+    {
+	super(tc.Dobject_prototype);
+	uint attributes = DontEnum;
+
+	Dobject f = tc.Dfunction_prototype;
+
+	Put(TEXT_constructor, tc.Dnumber_constructor, attributes);
+
+	static NativeFunctionData nfd[] =
+	[
+	    {   &TEXT_toString, &Dnumber_prototype_toString, 1 },
+	    // Permissible to use toString()
+	    {   &TEXT_toLocaleString, &Dnumber_prototype_toLocaleString, 1 },
+	    {   &TEXT_valueOf, &Dnumber_prototype_valueOf, 0 },
+	    {   &TEXT_toFixed, &Dnumber_prototype_toFixed, 1 },
+	    {   &TEXT_toExponential, &Dnumber_prototype_toExponential, 1 },
+	    {   &TEXT_toPrecision, &Dnumber_prototype_toPrecision, 1 },
+	];
+
+	DnativeFunction.init(this, nfd, attributes);
+    }
+}
+
+
+/* ===================== Dnumber ==================== */
+
+class Dnumber : Dobject
+{
+    this(d_number n)
+    {
+	super(getPrototype());
+	classname = TEXT_Number;
+	value.putVnumber(n);
+    }
+
+    this(Dobject prototype)
+    {
+	super(prototype);
+	classname = TEXT_Number;
+	value.putVnumber(0);
+    }
+
+    static Dfunction getConstructor()
+    {
+	ThreadContext *tc = ThreadContext.getThreadContext();
+	assert(tc);
+	return tc.Dnumber_constructor;
+    }
+
+    static Dobject getPrototype()
+    {
+	ThreadContext *tc = ThreadContext.getThreadContext();
+	assert(tc);
+	return tc.Dnumber_prototype;
+    }
+
+    static void init(ThreadContext *tc)
+    {
+	tc.Dnumber_constructor = new Dnumber_constructor(tc);
+	tc.Dnumber_prototype = new Dnumber_prototype(tc);
+
+	tc.Dnumber_constructor.Put(TEXT_prototype, tc.Dnumber_prototype, DontEnum | DontDelete | ReadOnly);
+    }
+}
+