{
    /**
    _test_jso_decode(): Test core _jso_decode_*() functions.
    The formatting is intentionally erratic here to simulate actual formatting deviations.
    JSOnion version: 1.0.0d
    */
    var json, expected, actual, expected_structure, actual_structure;
    
    ////Primitives
    
    //_jso_decode_string(): Empty string
    json = '""';
    expected = __jso_gmt_tuple("", 3);
    actual = _jso_decode_string(json, 1);
    assert_equal(expected, actual, "_jso_decode_string() failed to decode an empty string!");
    
    //_jso_decode_string(): Small string
    json = '"key"  ';
    expected = __jso_gmt_tuple("key", 6);
    actual = _jso_decode_string(json, 1);
    assert_equal(expected, actual, "_jso_decode_string() failed to decode a small string!");
    
    //_jso_decode_string(): Simple string
    json = '  "The quick brown fox jumps over the lazy dog." ';
    expected = __jso_gmt_tuple("The quick brown fox jumps over the lazy dog.", 49);
    actual = _jso_decode_string(json, 1);
    assert_equal(expected, actual, "_jso_decode_string() failed to decode a simple string!");
    
    //_jso_decode_string(): Escape characters
    json = ' "\"\\\b\f\n\r\t\u003A"';
    expected = __jso_gmt_tuple('"\' + chr(8) + chr(12) + chr(10) + chr(13) + chr(9) + chr($003a), 24);
    actual = _jso_decode_string(json, 1);
    assert_equal(expected, actual, "_jso_decode_string() failed to decode a string with escape characters!");
    
    //_jso_decode_string(): Mixed characters
    json = ' "\"\\\bWaahoo\f\n\r\tnegg\u003a"';
    expected = __jso_gmt_tuple('"\' + chr(8) + "Waahoo" + chr(12) + chr(10) + chr(13) + chr(9) + "negg" + chr($003a), 34);
    actual = _jso_decode_string(json, 1);
    assert_equal(expected, actual, "_jso_decode_string() failed to decode a string with mixed characters!");
    
    //_jso_decode_boolean(): True
    json = 'true';
    expected = __jso_gmt_tuple(true, 5);
    actual = _jso_decode_boolean(json, 1);
    assert_equal(expected, actual, "_jso_decode_boolean() failed to decode true!");
    
    //_jso_decode_boolean(): False
    json = '  false  ';
    expected = __jso_gmt_tuple(false, 8);
    actual = _jso_decode_boolean(json, 1);
    assert_equal(expected, actual, "_jso_decode_boolean() failed to decode false!");
    
    //_jso_decode_real(): Zero
    json = '0';
    expected = __jso_gmt_tuple(0, 2);
    actual = _jso_decode_real(json, 1);
    assert_equal(expected, actual, "_jso_decode_real() failed to decode standard zero!");
    
    //_jso_decode_real(): Signed zero
    json = '  +0 ';
    expected = __jso_gmt_tuple(0, 5);
    actual = _jso_decode_real(json, 1);
    assert_equal(expected, actual, "_jso_decode_real() failed to decode signed zero!");
    
    //_jso_decode_real(): Signed zero with decimal digits
    json = ' -0.000';
    expected = __jso_gmt_tuple(0, 8);
    actual = _jso_decode_real(json, 1);
    assert_equal(expected, actual, "_jso_decode_real() failed to decode signed zero with decimal digits!");
    
    //_jso_decode_real(): Positive real
    json = '3.14159';
    expected = __jso_gmt_tuple(3.14159, 8);
    actual = _jso_decode_real(json, 1);
    assert_equal(expected, actual, "_jso_decode_real() failed to decode positive real number!");
    
    //_jso_decode_real(): Negative real
    json = ' -2.71828';
    expected = __jso_gmt_tuple(-2.71828, 10);
    actual = _jso_decode_real(json, 1);
    assert_equal(expected, actual, "_jso_decode_real() failed to decode negative real number!");
    
    //_jso_decode_real(): Positive real with positive exponent
    json = ' 3.14159e2';
    expected = __jso_gmt_tuple(3.14159*100, 11);
    actual = _jso_decode_real(json, 1);
    assert_equal(expected, actual, "_jso_decode_real() failed to decode positive real number with positive exponent!");
    
    //_jso_decode_real(): Negative real with positive exponent
    json = ' -2.71828E2';
    expected = __jso_gmt_tuple(-2.71828*100, 12);
    actual = _jso_decode_real(json, 1);
    assert_equal(expected, actual, "_jso_decode_real() failed to decode negative real number with positive exponent!");
    
    //_jso_decode_real(): Positive real with negative exponent
    json = ' 314.159e-2';
    expected = __jso_gmt_tuple(3.14159, 12);
    actual = _jso_decode_real(json, 1);
    assert_equal(expected, actual, "_jso_decode_real() failed to decode positive real number with negative exponent!");
    
    //_jso_decode_real(): Negative real with negative exponent
    json = ' -271.828E-2';
    expected = __jso_gmt_tuple(-2.71828, 13);
    actual = _jso_decode_real(json, 1);
    assert_equal(expected, actual, "_jso_decode_real() failed to decode negative real number with negative exponent!");
    
    //_jso_decode_real(): Positive integer
    json = ' +1729';
    expected = __jso_gmt_tuple(1729, 7);
    actual = _jso_decode_real(json, 1);
    assert_equal(expected, actual, "_jso_decode_real() failed to decode positive integer!");
    
    //_jso_decode_real(): Negative integer
    json = '-583';
    expected = __jso_gmt_tuple(-583, 5);
    actual = _jso_decode_real(json, 1);
    assert_equal(expected, actual, "_jso_decode_real() failed to decode negative integer!");
    
    //_jso_decode_integer(): Zero
    json = ' 0  ';
    expected = __jso_gmt_tuple(0, 3);
    actual = _jso_decode_integer(json, 1);
    assert_equal(expected, actual, "_jso_decode_integer() failed to decode zero!");
    
    //_jso_decode_integer(): Positive integer
    json = ' 1729   ';
    expected = __jso_gmt_tuple(1729, 6);
    actual = _jso_decode_integer(json, 1);
    assert_equal(expected, actual, "_jso_decode_integer() failed to decode positive integer!");
    
    //_jso_decode_integer(): Negative integer
    json = '   -583';
    expected = __jso_gmt_tuple(-583, 8);
    actual = _jso_decode_integer(json, 1);
    assert_equal(expected, actual, "_jso_decode_integer() failed to decode negative integer!");
    
    
    ////Data structures
    
    //_jso_decode_map(): Empty map #1
    json = '{}';
    expected_structure = jso_new_map();
    expected = __jso_gmt_tuple(expected_structure, 3);
    actual = _jso_decode_map(json, 1);
    actual_structure = __jso_gmt_elem(actual, 0);
    assert_equal(__jso_gmt_elem(expected, 1), __jso_gmt_elem(actual, 1), "_jso_decode_map() didn't stop at the right place! (#1)");
    assert_equal(actual_structure, __jso_gmt_elem(actual, 0), "_jso_decode_map() didn't include the right prefix! (#1)");
    assert_true(jso_compare_maps(expected_structure, actual_structure), "_jso_decode_map() failed to decode an empty map! (#1)");
    jso_cleanup_map(expected_structure);
    jso_cleanup_map(actual_structure);
    
    //_jso_decode_map(): Empty map #2
    json = '  {  }  ';
    expected_structure = jso_new_map();
    expected = __jso_gmt_tuple(expected_structure, 7);
    actual = _jso_decode_map(json, 1);
    actual_structure = __jso_gmt_elem(actual, 0);
    assert_equal(__jso_gmt_elem(expected, 1), __jso_gmt_elem(actual, 1), "_jso_decode_map() didn't stop at the right place! (#2)");
    assert_equal(actual_structure, __jso_gmt_elem(actual, 0), "_jso_decode_map() didn't include the right prefix! (#2)");
    assert_true(jso_compare_maps(expected_structure, actual_structure), "_jso_decode_map() failed to decode an empty map! (#2)");
    jso_cleanup_map(expected_structure);
    jso_cleanup_map(actual_structure);
    
    //_jso_decode_map(): One-entry map
    json = ' {"key": "value"} ';
    expected_structure = jso_new_map();
    expected = __jso_gmt_tuple(expected_structure, 18);
    jso_map_add_string(expected_structure, "key", "value");
    actual = _jso_decode_map(json, 1);
    actual_structure = __jso_gmt_elem(actual, 0);
    assert_equal(__jso_gmt_elem(expected, 1), __jso_gmt_elem(actual, 1), "_jso_decode_map() didn't stop at the right place! (one-entry map)");
    assert_equal(actual_structure, __jso_gmt_elem(actual, 0), "_jso_decode_map() didn't include the right prefix! (one-entry map)");
    assert_true(jso_compare_maps(expected_structure, actual_structure), "_jso_decode_map() failed to decode a one-entry map!");
    jso_cleanup_map(expected_structure);
    jso_cleanup_map(actual_structure);
    
    //_jso_decode_map(): Multi-entry map
    json = ' {"key"  :  "value", "pi":3.14,  "bool" : true} ';
    expected_structure = jso_new_map();
    expected = __jso_gmt_tuple(expected_structure, 48);
    jso_map_add_string(expected_structure, "key", "value");
    jso_map_add_real(expected_structure, "pi", 3.14);
    jso_map_add_boolean(expected_structure, "bool", true);
    actual = _jso_decode_map(json, 1);
    actual_structure = __jso_gmt_elem(actual, 0);
    assert_equal(__jso_gmt_elem(expected, 1), __jso_gmt_elem(actual, 1), "_jso_decode_map() didn't stop at the right place! (multi-entry map)");
    assert_equal(actual_structure, __jso_gmt_elem(actual, 0), "_jso_decode_map() didn't include the right prefix! (multi-entry map)");
    assert_true(jso_compare_maps(expected_structure, actual_structure), "_jso_decode_map() failed to decode a multi-entry map!");
    jso_cleanup_map(expected_structure);
    jso_cleanup_map(actual_structure);
    
    //_jso_decode_map(): Nested maps
    var submap;
    json = '{ "waahoo" : { "woohah" : 3 } , "woohah" : 4 }';
    expected_structure = jso_new_map();
    expected = __jso_gmt_tuple(expected_structure, 47);
    jso_map_add_integer(expected_structure, "woohah", 4);
    submap = jso_new_map();
    jso_map_add_integer(submap, "woohah", 3);
    jso_map_add_submap(expected_structure, "waahoo", submap);
    actual = _jso_decode_map(json, 1);
    actual_structure = __jso_gmt_elem(actual, 0);
    assert_equal(__jso_gmt_elem(expected, 1), __jso_gmt_elem(actual, 1), "_jso_decode_map() didn't stop at the right place! (nested map)");
    assert_equal(actual_structure, __jso_gmt_elem(actual, 0), "_jso_decode_map() didn't include the right prefix! (nested map)");
    assert_true(jso_compare_maps(expected_structure, actual_structure), "_jso_decode_map() failed to decode a nested map!");
    jso_cleanup_map(expected_structure);
    jso_cleanup_map(actual_structure);
    
    //_jso_decode_map(): Map with nested lists
    var sublist, subsublist;
    json = '{ "waahoo" : [ "woohah", [true] ] , "woohah" : 4 }';
    expected_structure = jso_new_map();
    expected = __jso_gmt_tuple(expected_structure, 51);
    jso_map_add_real(expected_structure, "woohah", 4);
    sublist = jso_new_list();
    jso_list_add_string(sublist, "woohah");
    subsublist = jso_new_list();
    jso_list_add_boolean(subsublist, true);
    jso_list_add_sublist(sublist, subsublist);
    jso_map_add_sublist(expected_structure, "waahoo", sublist);
    actual = _jso_decode_map(json, 1);
    actual_structure = __jso_gmt_elem(actual, 0);
    assert_equal(__jso_gmt_elem(expected, 1), __jso_gmt_elem(actual, 1), "_jso_decode_map() didn't stop at the right place! (map with nested lists)");
    assert_equal(actual_structure, __jso_gmt_elem(actual, 0), "_jso_decode_map() didn't include the right prefix! (map with nested lists)");
    assert_true(jso_compare_maps(expected_structure, actual_structure), "_jso_decode_map() failed to decode a map with nested lists!");
    jso_cleanup_map(expected_structure);
    jso_cleanup_map(actual_structure);
    
    //_jso_decode_map(): Mix-up nested map
    var sublist;
    json = ' { "waahoo" : [{}, "a", 1]  }';
    expected_structure = jso_new_map();
    expected = __jso_gmt_tuple(expected_structure, 30);
    sublist = jso_new_list();
    jso_list_add_submap(sublist, jso_new_map());
    jso_list_add_string(sublist, "a");
    jso_list_add_real(sublist, 1);
    jso_map_add_sublist(expected_structure, "waahoo", sublist);
    actual = _jso_decode_map(json, 1);
    actual_structure = __jso_gmt_elem(actual, 0);
    assert_equal(__jso_gmt_elem(expected, 1), __jso_gmt_elem(actual, 1), "_jso_decode_map() didn't stop at the right place! (mix-up nested map)");
    assert_equal(actual_structure, __jso_gmt_elem(actual, 0), "_jso_decode_map() didn't include the right prefix! (mix-up nested map)");
    assert_true(jso_compare_maps(expected_structure, actual_structure), "_jso_decode_map() failed to decode a mix-up nested map!");
    jso_cleanup_map(expected_structure);
    jso_cleanup_map(actual_structure);
    
    //_jso_decode_list(): Empty list #1
    json = '[]';
    expected_structure = jso_new_list();
    expected = __jso_gmt_tuple(expected_structure, 3);
    actual = _jso_decode_list(json, 1);
    actual_structure = __jso_gmt_elem(actual, 0);
    assert_equal(__jso_gmt_elem(expected, 1), __jso_gmt_elem(actual, 1), "_jso_decode_list() didn't stop at the right place! (#1)");
    assert_equal(actual_structure, __jso_gmt_elem(actual, 0), "_jso_decode_list() didn't include the right prefix! (#1)");
    assert_true(jso_compare_lists(expected_structure, actual_structure), "_jso_decode_list() failed to decode an empty list! (#1)");
    jso_cleanup_list(expected_structure);
    jso_cleanup_list(actual_structure)
    
    //_jso_decode_list(): Empty list #2
    json = ' [  ] ';
    expected_structure = jso_new_list();
    expected = __jso_gmt_tuple(expected_structure, 6);
    actual = _jso_decode_list(json, 1);
    actual_structure = __jso_gmt_elem(actual, 0);
    assert_equal(__jso_gmt_elem(expected, 1), __jso_gmt_elem(actual, 1), "_jso_decode_list() didn't stop at the right place! (#2)");
    assert_equal(actual_structure, __jso_gmt_elem(actual, 0), "_jso_decode_list() didn't include the right prefix! (#2)");
    assert_true(jso_compare_lists(expected_structure, actual_structure), "_jso_decode_list() failed to decode an empty list! (#2)");
    jso_cleanup_list(expected_structure);
    jso_cleanup_list(actual_structure);
    
    //_jso_decode_list(): One-entry list
    json = '[3]';
    expected_structure = jso_new_list();
    expected = __jso_gmt_tuple(expected_structure, 4);
    jso_list_add_integer(expected_structure, 3);
    actual = _jso_decode_list(json, 1);
    actual_structure = __jso_gmt_elem(actual, 0);
    assert_equal(__jso_gmt_elem(expected, 1), __jso_gmt_elem(actual, 1), "_jso_decode_list() didn't stop at the right place! (one-entry list)");
    assert_equal(actual_structure, __jso_gmt_elem(actual, 0), "_jso_decode_list() didn't include the right prefix! (one-entry list)");
    assert_true(jso_compare_lists(expected_structure, actual_structure), "_jso_decode_list() failed to decode a one-entry list!");
    jso_cleanup_list(expected_structure);
    jso_cleanup_list(actual_structure);
   
    //_jso_decode_list(): Multi-entry list
    json = ' [4,"multi-entry",true]';
    expected_structure = jso_new_list();
    expected = __jso_gmt_tuple(expected_structure, 24);
    jso_list_add_real(expected_structure, 4);
    jso_list_add_string(expected_structure, "multi-entry");
    jso_list_add_boolean(expected_structure, true);
    actual = _jso_decode_list(json, 1);
    actual_structure = __jso_gmt_elem(actual, 0);
    assert_equal(__jso_gmt_elem(expected, 1), __jso_gmt_elem(actual, 1), "_jso_decode_list() didn't stop at the right place! (multi-entry list)");
    assert_equal(actual_structure, __jso_gmt_elem(actual, 0), "_jso_decode_list() didn't include the right prefix! (multi-entry list)");
    assert_true(jso_compare_lists(expected_structure, actual_structure), "_jso_decode_list() failed to decode a multi-entry list!");
    jso_cleanup_list(expected_structure);
    jso_cleanup_list(actual_structure);
    
    //_jso_decode_list(): Nested list
    var sublist;
    json = ' [ [], 3, false, ["waahoo"]]';
    expected_structure = jso_new_list();
    expected = __jso_gmt_tuple(expected_structure, 29);
    jso_list_add_sublist(expected_structure, jso_new_list());
    jso_list_add_integer(expected_structure, 3);
    jso_list_add_boolean(expected_structure, false);
    sublist = jso_new_list();
    jso_list_add_string(sublist, "waahoo");
    jso_list_add_sublist(expected_structure, sublist);
    actual = _jso_decode_list(json, 1);
    actual_structure = __jso_gmt_elem(actual, 0);
    assert_equal(__jso_gmt_elem(expected, 1), __jso_gmt_elem(actual, 1), "_jso_decode_list() didn't stop at the right place! (nested list)");
    assert_equal(actual_structure, __jso_gmt_elem(actual, 0), "_jso_decode_list() didn't include the right prefix! (nested list)");
    assert_true(jso_compare_lists(expected_structure, actual_structure), "_jso_decode_list() failed to decode a nested list!");
    jso_cleanup_list(expected_structure);
    jso_cleanup_list(actual_structure);
    
    //_jso_decode_list(): List with nested maps
    var submap;
    json = ' [3, false, { "waahoo":"woo"}]';
    expected_structure = jso_new_list();
    expected = __jso_gmt_tuple(expected_structure, 31);
    jso_list_add_integer(expected_structure, 3);
    jso_list_add_boolean(expected_structure, false);
    submap = jso_new_map();
    jso_map_add_string(submap, "waahoo", "woo");
    jso_list_add_submap(expected_structure, submap);
    actual = _jso_decode_list(json, 1);
    actual_structure = __jso_gmt_elem(actual, 0);
    assert_equal(__jso_gmt_elem(expected, 1), __jso_gmt_elem(actual, 1), "_jso_decode_list() didn't stop at the right place! (list with nested maps)");
    assert_equal(actual_structure, __jso_gmt_elem(actual, 0), "_jso_decode_list() didn't include the right prefix! (list with nested maps)");
    assert_true(jso_compare_lists(expected_structure, actual_structure), "_jso_decode_list() failed to decode a list with nested maps!");
    jso_cleanup_list(expected_structure);
    jso_cleanup_list(actual_structure);
    
    //_jso_decode_list(): Mix-up nested list
    var submap;
    json = '[{}, {"a":[]}]';
    expected_structure = jso_new_list();
    expected = __jso_gmt_tuple(expected_structure, 15);
    jso_list_add_submap(expected_structure, jso_new_map());
    submap = jso_new_map();
    jso_map_add_sublist(submap, "a", jso_new_list());
    jso_list_add_submap(expected_structure, submap);
    actual = _jso_decode_list(json, 1);
    actual_structure = __jso_gmt_elem(actual, 0);
    assert_equal(__jso_gmt_elem(expected, 1), __jso_gmt_elem(actual, 1), "_jso_decode_list() didn't stop at the right place! (mix-up nested list)");
    assert_equal(actual_structure, __jso_gmt_elem(actual, 0), "_jso_decode_list() didn't include the right prefix! (mix-up nested list)");
    assert_true(jso_compare_lists(expected_structure, actual_structure), "_jso_decode_list() failed to decode a mix-up nested list!");
    jso_cleanup_list(expected_structure);
    jso_cleanup_list(actual_structure);
}
