//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");

var tests = [
  {
    name: "Decimal literal support",
    body: function () {
        assert.areEqual(1234, 1_234, "1234 === 1_234");
        assert.areEqual(1234, 1_2_3_4, "1234 === 1_2_3_4");
        assert.areEqual(1234.567, 1_2_3_4.5_6_7, "1234.567 === 1_2_3_4.5_6_7");

        assert.areEqual(-1234, -1_2_34, "-1234 === -1_2_34");
        assert.areEqual(-12.34, -1_2.3_4, "-12.34 === -1_2.3_4");
    }
  },
  {
    name: "Decimal literal with exponent",
    body: function () {
        assert.areEqual(1e100, 1e1_00, "1e100 === 1e1_00");
        assert.areEqual(Infinity, 1e1_0_0_0, "Infinity === 1e1_0_0_0");

        assert.areEqual(123.456e23, 1_2_3.4_5_6e2_3, "123.456e23 === 1_2_3.4_5_6e2_3");
        assert.areEqual(123.456e001, 1_2_3.4_5_6e0_0_1, "123.456e001 === 1_2_3.4_5_6e0_0_1");
    }
  },
  {
    name: "Decimal literal bad syntax",
    body: function () {
        // Decimal left-part only with numeric separators
        assert.throws(()=>eval('1__2'), SyntaxError, "Multiple numeric separators in a row are not allowed");
        assert.throws(()=>eval('1_2____3'), SyntaxError, "Multiple numeric separators in a row are not allowed");
        assert.throws(()=>eval('1_'), SyntaxError, "Decimal may not end in a numeric separator");
        assert.throws(()=>eval('1__'), SyntaxError, "Decimal may not end in a numeric separator");
        assert.throws(()=>eval('__1'), ReferenceError, "Decimal may not begin with a numeric separator");
        assert.throws(()=>eval('_1'), ReferenceError, "Decimal may not begin with a numeric separator");

        // Decimal with right-part with numeric separators
        assert.throws(()=>eval('1.0__0'), SyntaxError, "Decimal right-part may not contain multiple contiguous numeric separators");
        assert.throws(()=>eval('1.0_0__2'), SyntaxError, "Decimal right-part may not contain multiple contiguous numeric separators");
        assert.throws(()=>eval('1._'), SyntaxError, "Decimal right-part may not be a single numeric separator");
        assert.throws(()=>eval('1.__'), SyntaxError, "Decimal right-part may not be multiple numeric separators");
        assert.throws(()=>eval('1._0'), SyntaxError, "Decimal right-part may not begin with a numeric separator");
        assert.throws(()=>eval('1.__0'), SyntaxError, "Decimal right-part may not begin with a numeric separator");
        assert.throws(()=>eval('1.0_'), SyntaxError, "Decimal right-part may not end with a numeric separator");
        assert.throws(()=>eval('1.0__'), SyntaxError, "Decimal right-part may not end with a numeric separator");

        // Decimal with both parts with numeric separators
        assert.throws(()=>eval('1_.0'), SyntaxError, "Decimal left-part may not end in numeric separator");
        assert.throws(()=>eval('1__.0'), SyntaxError, "Decimal left-part may not end in numeric separator");
        assert.throws(()=>eval('1__2.0'), SyntaxError, "Decimal left-part may not contain multiple contiguous numeric separators");

        // Decimal with exponent with numeric separators
        assert.throws(()=>eval('1_e10'), SyntaxError, "Decimal left-part may not end in numeric separator");
        assert.throws(()=>eval('1e_1'), SyntaxError, "Exponent may not begin with numeric separator");
        assert.throws(()=>eval('1e__1'), SyntaxError, "Exponent may not begin with numeric separator");
        assert.throws(()=>eval('1e1_'), SyntaxError, "Exponent may not end with numeric separator");
        assert.throws(()=>eval('1e1__'), SyntaxError, "Exponent may not end with numeric separator");
        assert.throws(()=>eval('1e1__2'), SyntaxError, "Exponent may not contain multiple contiguous numeric separators");

        // Decimal big ints with numeric separators
        assert.throws(()=>eval('1_n'), SyntaxError);
    }
  },
  {
    name: "Strings parsed as number do not support numeric separators for decimal literals",
    body: function () {
        assert.areEqual(NaN, Number('12_34'), "NaN === Number('12_34')");
        assert.areEqual(NaN, Number('12e3_4'), "NaN === Number('12e3_4')");
        assert.areEqual(NaN, Number('1234.45_67'), "NaN === Number('1234.45_67')");
        assert.areEqual(NaN, Number('1234.45e6_7'), "NaN === Number('1234.45e6_7')");

        assert.areEqual(1, parseInt('1_2'), "1 === parseInt('1_2')");
        assert.areEqual(1, parseInt('1e2_3'), "1 === parseInt('1e2_3')");
        assert.areEqual(12, parseInt('12.3_4'), "1 === parseInt('12.3_4')");
        assert.areEqual(12, parseInt('12.34e5_6'), "1 === parseInt('12.34e5_6')");

        assert.areEqual(1, parseFloat('1_2'), "1 === parseFloat('1_2')");
        assert.areEqual(1e2, parseFloat('1e2_3'), "1 === parseFloat('1e2_3')");
        assert.areEqual(12.3, parseFloat('12.3_4'), "1 === parseFloat('12.3_4')");
        assert.areEqual(12.34e5, parseFloat('12.34e5_6'), "1 === parseFloat('12.34e5_6')");
    }
  },
  {
    name: "Binary literal support",
    body: function () {
        assert.areEqual(0b00, 0b0_0, "0b00 === 0b0_0");
        assert.areEqual(0b11, 0b1_1, "0b11 === 0b1_1");
        assert.areEqual(0b10, 0b1_0, "0b10 === 0b1_0");
        assert.areEqual(0b01, 0b0_1, "0b01 === 0b0_1");
        assert.areEqual(0b0001, 0b000_1, "0b0001 === 0b000_1");
        assert.areEqual(0b0000, 0b000_0, "0b0000 === 0b000_0");
        assert.areEqual(0b000011110000, 0b0000_1111_0000, "0b000011110000 === 0b0000_1111_0000");
        assert.areEqual(0b000011110000, 0b0_0_0_0_1_111_00_00, "0b000011110000 === 0b0_0_0_0_1_111_00_00");
    }
  },
  {
    name: "Binary literal bad syntax",
    body: function () {
        assert.throws(()=>eval('0b_'), SyntaxError, "'_' cannot immediately follow 0b");
        assert.throws(()=>eval('0b__'), SyntaxError, "'_' cannot immediately follow 0b");
        assert.throws(()=>eval('0b_1'), SyntaxError, "Binary literal may not begin with numeric separator");
        assert.throws(()=>eval('0b_0'), SyntaxError, "Binary literal may not begin with numeric separator");
        assert.throws(()=>eval('0b__1'), SyntaxError, "Binary literal may not begin with numeric separator");
        assert.throws(()=>eval('0b__0'), SyntaxError, "Binary literal may not begin with numeric separator");
        assert.throws(()=>eval('0b1_'), SyntaxError, "Binary literal may not end with numeric separator");
        assert.throws(()=>eval('0b0_'), SyntaxError, "Binary literal may not end with numeric separator");
        assert.throws(()=>eval('0b1__'), SyntaxError, "Binary literal may not end with numeric separator");
        assert.throws(()=>eval('0b0__'), SyntaxError, "Binary literal may not end with numeric separator");
        assert.throws(()=>eval('0b1__1'), SyntaxError, "Multiple numeric separator characters may not follow each other");
        assert.throws(()=>eval('0b0__0'), SyntaxError, "Multiple numeric separator characters may not follow each other");
        assert.throws(()=>eval('0b000__1'), SyntaxError, "Multiple numeric separator characters may not follow each other");
        assert.throws(()=>eval('0b000_a'), SyntaxError, "After initial zeroes, a numeric separator followed by an invalid character");
    }
  },
  {
    name: "Strings parsed as number do not support numeric separators for binary literals",
    body: function () {
        assert.areEqual(NaN, Number('0b0_1'), "NaN === Number('0b0_1')");
        assert.areEqual(NaN, Number('0b1_0'), "NaN === Number('0b1_0')");
        assert.areEqual(NaN, Number('0b0_0'), "NaN === Number('0b0_0')");
        assert.areEqual(NaN, Number('0b1_1'), "NaN === Number('0b1_1')");

        assert.areEqual(0, parseInt('0b1_0'), "0 === parseInt('0b1_0')");

        assert.areEqual(0, parseFloat('0b1_0'), "0 === parseFloat('0b1_0')");
    }
  },
  {
    name: "Hex numeric literal support",
    body: function () {
        assert.areEqual(0x00, 0x0_0, "0x00 === 0x0_0");
        assert.areEqual(0x1f, 0x1_f, "0x1f === 0x1_f");
        assert.areEqual(0x000000Ae1, 0x00_0_000_A_e1, "0x000000Ae1 === 0x00_0_000_A_e1");
        assert.areEqual(0xaabbccdd, 0xaa_bb_cc_dd, "0xaabbccdd === 0xaa_bb_cc_dd");
    }
  },
  {
    name: "Hex numeric literal bad syntax",
    body: function () {
        assert.throws(()=>eval('0x_'), SyntaxError, "'_' cannot immediately follow 0x");
        assert.throws(()=>eval('0x__'), SyntaxError, "'_' cannot immediately follow 0x");
        assert.throws(()=>eval('0x_1'), SyntaxError, "Hex literal may not begin with numeric separator");
        assert.throws(()=>eval('0x_0'), SyntaxError, "Hex literal may not begin with numeric separator");
        assert.throws(()=>eval('0x__1'), SyntaxError, "Hex literal may not begin with numeric separator");
        assert.throws(()=>eval('0x__0'), SyntaxError, "Hex literal may not begin with numeric separator");
        assert.throws(()=>eval('0x1_'), SyntaxError, "Hex literal may not end with numeric separator");
        assert.throws(()=>eval('0x0_'), SyntaxError, "Hex literal may not end with numeric separator");
        assert.throws(()=>eval('0x1__'), SyntaxError, "Hex literal may not end with numeric separator");
        assert.throws(()=>eval('0x0__'), SyntaxError, "Hex literal may not end with numeric separator");
        assert.throws(()=>eval('0x1__1'), SyntaxError, "Multiple numeric separator characters may not follow each other");
        assert.throws(()=>eval('0x0__0'), SyntaxError, "Multiple numeric separator characters may not follow each other");
        assert.throws(()=>eval('0x000__1'), SyntaxError, "Multiple numeric separator characters may not follow each other");
        assert.throws(()=>eval('0x000_q'), SyntaxError, "After initial zeroes, a numeric separator followed by an invalid character");
    }
  },
  {
    name: "Strings parsed as number do not support numeric separators for hex literals",
    body: function () {
        assert.areEqual(NaN, Number('0x0_ff'), "NaN === Number('0x0_ff')");
        assert.areEqual(NaN, Number('0xF_F'), "NaN === Number('0xF_F')");

        assert.areEqual(0, parseInt('0x0_ff'), "0 === parseInt('0x0_ff')");
        assert.areEqual(15, parseInt('0xf_00f'), "15 === parseInt('0xf_00f')");

        assert.areEqual(0, parseFloat('0x0_ff'), "0 === parseFloat('0x0_ff')");
        assert.areEqual(0, parseFloat('0x1_fe'), "0 === parseFloat('0x1_fe')");
    }
  },
  {
    name: "Octal literal support",
    body: function () {
        assert.areEqual(0o00, 0o0_0, "0o00 === 0o0_0");
        assert.areEqual(0o11, 0o1_1, "0o11 === 0o1_1");
        assert.areEqual(0o10, 0o1_0, "0o10 === 0o1_0");
        assert.areEqual(0o01, 0o0_1, "0o01 === 0o0_1");
        assert.areEqual(0o0001, 0o000_1, "0o0001 === 0o000_1");
        assert.areEqual(0o0000, 0o000_0, "0o0000 === 0o000_0");
        assert.areEqual(0o000011110000, 0o0000_1111_0000, "0o000011110000 === 0o0000_1111_0000");
        assert.areEqual(0o000011110000, 0o0_0_0_0_1_111_00_00, "0o000011110000 === 0o0_0_0_0_1_111_00_00");
    }
  },
  {
    name: "Octal literal bad syntax",
    body: function () {
        assert.throws(()=>eval('0o_'), SyntaxError, "'_' cannot immediately follow 0o");
        assert.throws(()=>eval('0o__'), SyntaxError, "'_' cannot immediately follow 0o");
        assert.throws(()=>eval('0o_1'), SyntaxError, "Octal literal may not begin with numeric separator");
        assert.throws(()=>eval('0o_0'), SyntaxError, "Octal literal may not begin with numeric separator");
        assert.throws(()=>eval('0o__1'), SyntaxError, "Octal literal may not begin with numeric separator");
        assert.throws(()=>eval('0o__0'), SyntaxError, "Octal literal may not begin with numeric separator");
        assert.throws(()=>eval('0o1_'), SyntaxError, "Octal literal may not end with numeric separator");
        assert.throws(()=>eval('0o0_'), SyntaxError, "Octal literal may not end with numeric separator");
        assert.throws(()=>eval('0o1__'), SyntaxError, "Octal literal may not end with numeric separator");
        assert.throws(()=>eval('0o0__'), SyntaxError, "Octal literal may not end with numeric separator");
        assert.throws(()=>eval('0o1__1'), SyntaxError, "Multiple numeric separator characters may not follow each other");
        assert.throws(()=>eval('0o0__0'), SyntaxError, "Multiple numeric separator characters may not follow each other");
        assert.throws(()=>eval('0o000__1'), SyntaxError, "Multiple numeric separator characters may not follow each other");
        assert.throws(()=>eval('0o000_a'), SyntaxError, "After initial zeroes, a numeric separator followed by an invalid character");
    }
  },
  {
    name: "Strings parsed as number do not support numeric separators for octal literals",
    body: function () {
        assert.areEqual(NaN, Number('0o0_1'), "NaN === Number('0o0_1')");
        assert.areEqual(NaN, Number('0o1_0'), "NaN === Number('0o1_0')");
        assert.areEqual(NaN, Number('0o0_0'), "NaN === Number('0o0_0')");
        assert.areEqual(NaN, Number('0o1_1'), "NaN === Number('0o1_1')");

        assert.areEqual(0, parseInt('0b1_0'), "0 === parseInt('0b1_0')");

        assert.areEqual(0, parseFloat('0b1_0'), "0 === parseFloat('0b1_0')");
    }
  },
  {
    name: "Legacy octal numeric literals do not support numeric separators",
    body: function () {
        assert.throws(()=>eval('0_1'), SyntaxError, "'_' cannot immediately follow 0 in legacy octal numeric literal");
        assert.throws(()=>eval('07_7'), SyntaxError, "Legacy octal numeric literals do not support numeric separator");
    }
  },
];

testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });
