scope {
/* Some sanity checks... */
assert 0xffff === 0xf_f__f___f;
assert 0b101 === 5;
assert 0b101 === 0b_1__0___1;
assert 0x13 === 0b_0001_0011;
assert 123 === 1_2__3;
assert 501 === 0o7_6__5;
}
scope {
//print(0.INT_MIN, 0.INT_MAX);
assert 0.INT_MIN < 0;
assert 0.INT_MAX > 0;
assert 0.INT_MIN-1 === 0.INT_MAX /* This (signed underflow/overflow) "should" be is platform-dependent: */;
assert 0.INT_MAX+1 === 0.INT_MIN /* This (signed underflow/overflow) "should" be is platform-dependent: */;
var i = 0;
assert 'integer' === typename i;
assert 0 === i.compare(0);
assert i.compare(1) < 0;
assert i.compare(-1) > 0;
assert 0.0 === i.toDouble();
assert 0 !== i.toDouble();
assert "0" === i.toJSONString();
assert "0" === i.toString();
assert "1" === 1.toString();
i = 42;
assert '*' === i.toChar();
}
scope {
var d = 0.0;
assert 'double' === typename d;
assert 0 === d.compare(0);
assert d.compare(1) < 0;
assert d.compare(-1) > 0;
assert 0 === d.toInt();
assert 0.0 !== d.toInt();
assert "0.0" === d.toJSONString();
assert "0.0" === d.toString();
assert "1.0" === 1.0.toString();
assert 1 === 0.5.ceil();
assert 0 === (-0.5).ceil(); // parens needed: dot has higher precedence than unary -.
//assert 0 === 0.ceil(); // INTEGER 0 doesn't have ceil()
assert 0 === 0.0.ceil();
assert -1 === (-1.0).ceil();
assert -1 === (-1.01).ceil();
assert 0 === (-0.999).ceil();
assert -2 === (-2.2).ceil();
assert 3 === 2.2.ceil();
assert 2 === 2.0.ceil();
assert 1 === 1.5.floor();
assert 1 === 1.0.floor();
assert 0 === 0.0.floor();
assert 0 === 0.5.floor();
assert -1 === (-0.5).floor();
assert -2 === (-1.5).floor();
}
scope {
1.0.prototype.twice = proc(){ return this * 2 };
assert 6.2 === 3.1.twice();
assert 8.0 === (2.0*2).twice();
//unset 0.0.prototype.twice; // unset doesn't like this, so...
var d = 0.0;
unset d.prototype.twice, d;
}
scope { // number.parseInt/Double/Number()
const pi = 0.parseInt, pf = 0.parseDouble, pn = 0.parseNumber;
assert 1 === pi(1);
assert 1 === pi('1');
assert undefined === pn('1_');
assert undefined === pi('1_');
assert 1 === pi('1.0');
assert -1 === pi('-1');
assert -1 === pi('-1.2');
assert undefined === pi('1-1');
assert 1.0 === pf(1);
assert 1.0 === pf('1');
assert 1.0 === pf(true);
assert 0.0 === pf(false);
assert 0.0 === pf('-0');
assert 1 === pn(1);
assert 1 === pn('1');
assert 1.0 === pn(1.0);
assert 1.0 === pn('1.0');
assert 0 === pn('-0');
assert 0.0 === pn(' + 0.0');
assert 56 === pn('0o70');
assert 56 === pn('0o7_0');
assert 56 === pn('0o_7__0');
assert undefined === pn('0o1_')/*trailing non-digit*/;
assert undefined === pn('0o18')/*trailing non-[octal-]digit*/;
assert -1 === pn('-0o1');
assert -1 === pn('-0x0001');
assert undefined === pn('-0x0001.')/*trailing non-digit*/;
assert undefined === pn(pn);
assert pf('1') === pn('1.0');
assert 'double' === typename pn("1.3") /* parseNumber() keeps the numeric type */;
assert 'integer' === typename pn("1") /* parseNumber() keeps the numeric type */;
assert 'integer' === typename pi("1.3") /* parseInt() reduces to an integer */;
}
scope { // number.nthPrime()
assert 2 === 0.nthPrime(1);
assert 7919 === 0.nthPrime(1000);
assert 'CWAL_RC_RANGE' === catch {0.nthPrime(0)}.codeString();
assert 'CWAL_RC_RANGE' === catch {0.nthPrime(1001)}.codeString();
assert 'CWAL_RC_MISUSE' === catch {0.nthPrime()}.codeString();
}
scope { // toString()
assert '1' === 1.toString();
assert '1.0' === 1.0.toString();
assert '1' === 1.0.toString('d');
assert '000c' === 12.toString('04x');
assert '000C' === 12.toString('04X');
assert '0e' === 0b11_10.toString('02x');
assert 'FBF' === 0x_f_B_f.toString('X');
assert '765' === 0o7_6_5.toString('o');
assert '765' === 0b111_110_101.toString('o');
}
scope {
assert 0x_f_0 === 2_4__0;
/* Interesting: we can't catch these errors because they trigger
in the tokenizer while slurping the {...} blocks. That means
that catch cannot really know that it "could" safely convert
these fatal syntax errors to non-fatal exceptions. */
//assert 0 === catch {1_}.message.indexOf('Malformed');
//assert catch {1_2.3}.message.indexOf('not legal') > 0;
/* So... to test these we'll wrap them in strings and eval them in
the 2nd-pass phase, as 2nd-pass eval knows that it can convert
fatal syntax errors to non-fatal exceptions. Note that using
eval=>{...} to capture them as strings cannot work here for the
same timing reason. */
assert 0 === catch -> {'1_'}.message.indexOf('Malformed numeric literal');
assert 0 === catch -> {'1.2_'}.message.indexOf('Malformed numeric literal');
assert 0 === catch -> {'0b1_'}.message.indexOf('Malformed binary');
assert 0 === catch -> {'0o1_'}.message.indexOf('Malformed octal');
assert 0 === catch -> {'0x1_'}.message.indexOf('Malformed hex');
assert catch -> {'1_2.3'}.message.indexOf('not legal in floating-point') > 0;
}
if(0.INT_MAX > 0xFFFF_FFFF /* 64 bit */) {
const largeI = 0xffff_ffff_ffff,
largeD = largeI.toDouble();
assert 0.parseNumber(largeD.toString()) === largeD;
assert 0.parseNumber(largeI.toString()) === largeI;
const largeD2 = 10.356 + 0xffffffffffff,
d2Str = largeD2.toString();
/* was "2.814749767107e14" prior to 20181127, but now
it's "281474976710665.4" (or thereabouts). */
assert d2Str.indexOf('e')<0 /* no scientific notation */;
assert 0.parseNumber(d2Str) === largeD2;
}