Mercurial > projects > dmdscript-tango
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); + } +} +