json-6
Version:
JSON for the ES6 era.
1,585 lines (1,318 loc) • 56.4 kB
JavaScript
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var json6 = createCommonjsModule(function (module, exports) {
const version = "1.1.1";
const VALUE_UNDEFINED = -1;
const VALUE_UNSET = 0;
const VALUE_NULL = 1;
const VALUE_TRUE = 2;
const VALUE_FALSE = 3;
const VALUE_STRING = 4;
const VALUE_NUMBER = 5;
const VALUE_OBJECT = 6;
const VALUE_ARRAY = 7;
const VALUE_NEG_NAN = 8;
const VALUE_NAN = 9;
const VALUE_NEG_INFINITY = 10;
const VALUE_INFINITY = 11; // const VALUE_DATE = 12 // unused yet
const VALUE_EMPTY = 13; // [,] makes an array with 'empty item'
const WORD_POS_RESET = 0;
const WORD_POS_TRUE_1 = 1;
const WORD_POS_TRUE_2 = 2;
const WORD_POS_TRUE_3 = 3;
const WORD_POS_FALSE_1 = 5;
const WORD_POS_FALSE_2 = 6;
const WORD_POS_FALSE_3 = 7;
const WORD_POS_FALSE_4 = 8;
const WORD_POS_NULL_1 = 9;
const WORD_POS_NULL_2 = 10;
const WORD_POS_NULL_3 = 11;
const WORD_POS_UNDEFINED_1 = 12;
const WORD_POS_UNDEFINED_2 = 13;
const WORD_POS_UNDEFINED_3 = 14;
const WORD_POS_UNDEFINED_4 = 15;
const WORD_POS_UNDEFINED_5 = 16;
const WORD_POS_UNDEFINED_6 = 17;
const WORD_POS_UNDEFINED_7 = 18;
const WORD_POS_UNDEFINED_8 = 19;
const WORD_POS_NAN_1 = 20;
const WORD_POS_NAN_2 = 21;
const WORD_POS_INFINITY_1 = 22;
const WORD_POS_INFINITY_2 = 23;
const WORD_POS_INFINITY_3 = 24;
const WORD_POS_INFINITY_4 = 25;
const WORD_POS_INFINITY_5 = 26;
const WORD_POS_INFINITY_6 = 27;
const WORD_POS_INFINITY_7 = 28;
const WORD_POS_FIELD = 29;
const WORD_POS_AFTER_FIELD = 30;
const WORD_POS_END = 31;
const CONTEXT_UNKNOWN = 0;
const CONTEXT_IN_ARRAY = 1; // const CONTEXT_IN_OBJECT = 2
const CONTEXT_OBJECT_FIELD = 3;
const CONTEXT_OBJECT_FIELD_VALUE = 4;
const contexts = [];
function getContext() {
return contexts.pop() || {
context: CONTEXT_UNKNOWN,
elements: null,
element_array: null
};
}
function dropContext(ctx) {
contexts.push(ctx);
}
const buffers = [];
function getBuffer() {
let buf = buffers.pop();
if (!buf) buf = {
buf: null,
n: 0
};else buf.n = 0;
return buf;
}
function dropBuffer(buf) {
buffers.push(buf);
}
const JSON6 = exports // istanbul ignore next
;
/*
let _DEBUG_LL = true;
let _DEBUG_PARSING = true;
let _DEBUG_PARSING_STACK = true;
const log = function(type) {
if (type === '_DEBUG_PARSING' && !_DEBUG_PARSING) {
return;
}
if (type === '_DEBUG_PARSING_STACK' && !_DEBUG_PARSING_STACK) {
return;
}
if (type === '_DEBUG_LL' && !_DEBUG_LL) {
return;
}
console.log.apply(console, [].slice.call(arguments, 1));
};
*/
JSON6.escape = function (string) {
let output = '';
if (!string) return string;
for (let n = 0; n < string.length; n++) {
const ch = string[n];
if (ch == '"' || ch == '\\' || ch == '`' || ch == '\'') {
output += '\\';
}
output += ch;
}
return output;
};
JSON6.begin = function (cb, reviver) {
const val = {
name: null,
// name of this value (if it's contained in an object)
value_type: VALUE_UNSET,
// value from above indiciating the type of this value
string: '',
// the string value of this value (strings and number types only)
contains: null
};
const pos = {
line: 1,
col: 1
};
let n = 0;
let word = WORD_POS_RESET,
status = true,
negative = false,
result = null,
elements = undefined,
element_array = [],
parse_context = CONTEXT_UNKNOWN,
comment = 0,
fromHex = false,
decimal = false,
exponent = false,
exponent_sign = false,
exponent_digit = false,
gatheringStringFirstChar = null,
gatheringString = false,
gatheringNumber = false,
stringEscape = false,
cr_escaped = false,
unicodeWide = false,
stringUnicode = false,
stringHex = false,
hex_char = 0,
hex_char_len = 0,
completed = false;
const context_stack = {
first: null,
last: null,
saved: null,
push(node) {
let recover = this.saved;
if (recover) {
this.saved = recover.next;
recover.node = node;
recover.next = null;
recover.prior = this.last;
} else {
recover = {
node: node,
next: null,
prior: this.last
};
}
if (!this.last) this.first = recover;
this.last = recover;
},
pop() {
const result = this.last;
if (!(this.last = result.prior)) this.first = null;
result.next = this.saved;
this.saved = result;
return result.node;
}
};
const inQueue = {
first: null,
last: null,
saved: null,
push(node) {
let recover = this.saved;
if (recover) {
this.saved = recover.next;
recover.node = node;
recover.next = null;
recover.prior = this.last;
} else {
recover = {
node: node,
next: null,
prior: this.last
};
}
if (!this.last) this.first = recover;else this.last.next = recover;
this.last = recover;
},
shift() {
const result = this.first;
if (!result) return null;
this.first = result.next;
if (!this.first) this.last = null;
result.next = this.saved;
this.saved = result; // node is in saved...
return result.node;
},
unshift(node) {
// usage in this module, recover will ALWAYS have a saved to use.
const recover = this.saved; //if( recover ) {
this.saved = recover.next;
recover.node = node;
recover.next = this.first;
recover.prior = null; //} else { recover = { node : node, next : this.first, prior : null }; }
if (!this.first) this.last = recover;
this.first = recover;
}
};
function throwEndError(leader
/* , c */
) {
throw new Error(`${leader} at ${n} [${pos.line}:${pos.col}]`);
}
return {
finalError() {
if (comment !== 0) {
// most of the time everything's good.
switch (comment) {
case 1:
return throwEndError("Comment began at end of document");
case 2:
console.log("Warning: '//' comment without end of line ended document");
break;
case 3:
return throwEndError("Open comment '/*' is missing close at end of document");
case 4:
return throwEndError("Incomplete '/* *' close at end of document");
}
}
if (gatheringString) throwEndError("Incomplete string");
},
value() {
this.finalError();
const r = result;
result = undefined;
return r;
},
reset() {
word = WORD_POS_RESET;
status = true;
if (inQueue.last) inQueue.last.next = inQueue.save;
inQueue.save = inQueue.first;
inQueue.first = inQueue.last = null;
if (context_stack.last) context_stack.last.next = context_stack.save;
context_stack.save = inQueue.first;
context_stack.first = context_stack.last = null; //= [];
element_array = null;
elements = undefined;
parse_context = CONTEXT_UNKNOWN;
val.value_type = VALUE_UNSET;
val.name = null;
val.string = '';
pos.line = 1;
pos.col = 1;
negative = false;
comment = 0;
completed = false;
gatheringString = false;
stringEscape = false; // string stringEscape intro
cr_escaped = false; // carraige return escaped
//stringUnicode = false; // reading \u
//unicodeWide = false; // reading \u{} in string
//stringHex = false; // reading \x in string
},
write(msg) {
let retcode;
if (msg !== undefined && typeof msg !== "string") msg = String(msg);
if (!status) throw new Error("Parser is in an error state, please reset.");
for (retcode = this._write(msg, false); retcode > 0; retcode = this._write()) {
this.finalError();
if (typeof reviver === 'function') (function walk(holder, key) {
const value = holder[key];
if (value && typeof value === 'object') {
for (const k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
const v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
})({
'': result
}, '');
cb(result);
result = undefined;
if (retcode < 2) break;
}
if (retcode) this.finalError();
},
_write(msg, complete_at_end) {
let input;
let buf;
let retval = 0;
function throwError(leader, c) {
throw new Error(`${leader} '${String.fromCodePoint(c)}' unexpected at ${n} (near '${buf.substr(n > 4 ? n - 4 : 0, n > 4 ? 3 : n - 1)}[${String.fromCodePoint(c)}]${buf.substr(n, 10)}') [${pos.line}:${pos.col}]`);
}
function RESET_VAL() {
val.value_type = VALUE_UNSET;
val.string = '';
}
function arrayPush() {
switch (val.value_type) {
case VALUE_NUMBER:
element_array.push((negative ? -1 : 1) * Number(val.string));
break;
case VALUE_STRING:
element_array.push(val.string);
break;
case VALUE_TRUE:
element_array.push(true);
break;
case VALUE_FALSE:
element_array.push(false);
break;
case VALUE_NEG_NAN:
element_array.push(-NaN);
break;
case VALUE_NAN:
element_array.push(NaN);
break;
case VALUE_NEG_INFINITY:
element_array.push(-Infinity);
break;
case VALUE_INFINITY:
element_array.push(Infinity);
break;
case VALUE_NULL:
element_array.push(null);
break;
case VALUE_UNDEFINED:
element_array.push(undefined);
break;
case VALUE_EMPTY:
element_array.push(undefined);
delete element_array[element_array.length - 1];
break;
case VALUE_OBJECT:
element_array.push(val.contains);
break;
case VALUE_ARRAY:
element_array.push(val.contains);
break;
}
}
function objectPush() {
switch (val.value_type) {
case VALUE_NUMBER:
elements[val.name] = (negative ? -1 : 1) * Number(val.string);
break;
case VALUE_STRING:
elements[val.name] = val.string;
break;
case VALUE_TRUE:
elements[val.name] = true;
break;
case VALUE_FALSE:
elements[val.name] = false;
break;
case VALUE_NEG_NAN:
elements[val.name] = -NaN;
break;
case VALUE_NAN:
elements[val.name] = NaN;
break;
case VALUE_NEG_INFINITY:
elements[val.name] = -Infinity;
break;
case VALUE_INFINITY:
elements[val.name] = Infinity;
break;
case VALUE_NULL:
elements[val.name] = null;
break;
case VALUE_UNDEFINED:
elements[val.name] = undefined;
break;
case VALUE_OBJECT:
elements[val.name] = val.contains;
break;
case VALUE_ARRAY:
elements[val.name] = val.contains;
break;
}
}
function gatherString(start_c) {
let retval = 0;
while (retval == 0 && n < buf.length) {
let str = buf.charAt(n);
const cInt = buf.codePointAt(n++);
if (cInt >= 0x10000) {
str += buf.charAt(n);
n++;
} //console.log( "gathering....", stringEscape, str, cInt, unicodeWide, stringHex, stringUnicode, hex_char_len );
pos.col++;
if (cInt == start_c) {
//( cInt == 34/*'"'*/ ) || ( cInt == 39/*'\''*/ ) || ( cInt == 96/*'`'*/ ) )
if (stringEscape) {
if (stringHex) throwError("Incomplete hexidecimal sequence", cInt);else if (unicodeWide) throwError("Incomplete long unicode sequence", cInt);else if (stringUnicode) throwError("Incomplete unicode sequence", cInt);
if (cr_escaped) {
cr_escaped = false; // \\ \r ' :end string, the backslash was used for \r
retval = 1; // complete string.
} else val.string += str; // escaped start quote
stringEscape = false;
} else {
// quote matches, not escaped, and not processing escape...
retval = 1;
}
} else if (stringEscape) {
if (unicodeWide) {
if (cInt == 125
/*'}'*/
) {
val.string += String.fromCodePoint(hex_char);
unicodeWide = false;
stringUnicode = false;
stringEscape = false;
continue;
}
hex_char *= 16;
if (cInt >= 48
/*'0'*/
&& cInt <= 57
/*'9'*/
) hex_char += cInt - 0x30;else if (cInt >= 65
/*'A'*/
&& cInt <= 70
/*'F'*/
) hex_char += cInt - 65 + 10;else if (cInt >= 97
/*'a'*/
&& cInt <= 102
/*'f'*/
) hex_char += cInt - 97 + 10;else {
throwError("(escaped character, parsing hex of \\u)", cInt);
}
continue;
} else if (stringHex || stringUnicode) {
if (hex_char_len === 0 && cInt === 123
/*'{'*/
) {
unicodeWide = true;
continue;
}
hex_char *= 16;
if (cInt >= 48
/*'0'*/
&& cInt <= 57
/*'9'*/
) hex_char += cInt - 0x30;else if (cInt >= 65
/*'A'*/
&& cInt <= 70
/*'F'*/
) hex_char += cInt - 65 + 10;else if (cInt >= 97
/*'a'*/
&& cInt <= 102
/*'f'*/
) hex_char += cInt - 97 + 10;else {
throwError(stringUnicode ? "(escaped character, parsing hex of \\u)" : "(escaped character, parsing hex of \\x)", cInt);
}
hex_char_len++;
if (stringUnicode) {
if (hex_char_len == 4) {
val.string += String.fromCodePoint(hex_char);
stringUnicode = false;
stringEscape = false;
}
} else if (hex_char_len == 2) {
val.string += String.fromCodePoint(hex_char);
stringHex = false;
stringEscape = false;
}
continue;
}
switch (cInt) {
case 13
/*'\r'*/
:
cr_escaped = true;
pos.col = 1;
continue;
case 0x2028: // LS (Line separator)
case 0x2029:
// PS (paragraph separator)
pos.col = 1;
// no return to get newline reset, so reset line pos.
// Fallthrough
case 10
/*'\n'*/
:
if (cr_escaped) {
// \\ \r \n
cr_escaped = false;
} else {
// \\ \n
pos.col = 1;
}
pos.line++;
break;
case 116
/*'t'*/
:
val.string += '\t';
break;
case 98
/*'b'*/
:
val.string += '\b';
break;
case 48
/*'0'*/
:
val.string += '\0';
break;
case 110
/*'n'*/
:
val.string += '\n';
break;
case 114
/*'r'*/
:
val.string += '\r';
break;
case 102
/*'f'*/
:
val.string += '\f';
break;
case 118
/*'v'*/
:
val.string += '\v';
break;
case 120
/*'x'*/
:
stringHex = true;
hex_char_len = 0;
hex_char = 0;
continue;
case 117
/*'u'*/
:
stringUnicode = true;
hex_char_len = 0;
hex_char = 0;
continue;
default:
val.string += str;
break;
} //console.log( "other..." );
stringEscape = false;
} else if (cInt === 92
/*'\\'*/
) {
stringEscape = true;
} else {
if (cr_escaped) {
cr_escaped = false; // \\ \r <any other character>
pos.line++;
pos.col = 2; // newline, plus one character.
}
val.string += str;
}
}
return retval;
}
function collectNumber() {
let _n;
while ((_n = n) < buf.length) {
const str = buf.charAt(_n);
const cInt = buf.codePointAt(n++);
if (cInt >= 0x10000) {
throwError("fault while parsing number;", cInt);
} //log('_DEBUG_PARSING', "in getting number:", n, cInt, String.fromCodePoint(cInt) );
if (cInt == 95
/*_*/
) continue;
pos.col++; // leading zeros should be forbidden.
if (cInt >= 48
/*'0'*/
&& cInt <= 57
/*'9'*/
) {
if (exponent) {
exponent_digit = true;
}
val.string += str;
} else if (cInt == 45
/*'-'*/
|| cInt == 43
/*'+'*/
) {
if (val.string.length == 0 || exponent && !exponent_sign && !exponent_digit) {
val.string += str;
exponent_sign = true;
} else {
status = false;
throwError("fault while parsing number;", cInt); // break;
}
} else if (cInt == 46
/*'.'*/
) {
if (!decimal && !fromHex && !exponent) {
val.string += str;
decimal = true;
} else {
status = false;
throwError("fault while parsing number;", cInt); // break;
}
} else if (fromHex && (cInt >= 95
/*'a'*/
&& cInt <= 102
/*'f'*/
|| cInt >= 65
/*'A'*/
&& cInt <= 70
/*'F'*/
)) {
val.string += str;
} else if (cInt == 120
/*'x'*/
|| cInt == 98
/*'b'*/
|| cInt == 111
/*'o'*/
|| cInt == 88
/*'X'*/
|| cInt == 66
/*'B'*/
|| cInt == 79
/*'O'*/
) {
// hex conversion.
if (!fromHex && val.string == '0') {
fromHex = true;
val.string += str;
} else {
status = false;
throwError("fault while parsing number;", cInt); // break;
}
} else if (cInt == 101
/*'e'*/
|| cInt == 69
/*'E'*/
) {
if (!exponent) {
val.string += str;
exponent = true;
} else {
status = false;
throwError("fault while parsing number;", cInt); // break;
}
} else {
if (cInt == 32
/*' '*/
|| cInt == 160
/*   */
|| cInt == 13 || cInt == 10 || cInt == 9 || cInt == 0xFEFF || cInt == 44
/*','*/
|| cInt == 125
/*'}'*/
|| cInt == 93
/*']'*/
|| cInt == 58
/*':'*/
) {
break;
} else {
if (complete_at_end) {
status = false;
throwError("fault while parsing number;", cInt);
}
break;
}
}
}
n = _n;
if (!complete_at_end && n == buf.length) {
gatheringNumber = true;
} else {
gatheringNumber = false;
val.value_type = VALUE_NUMBER;
if (parse_context == CONTEXT_UNKNOWN) {
completed = true;
}
}
}
if (!status) return -1;
if (msg && msg.length) {
input = getBuffer();
input.buf = msg;
inQueue.push(input);
} else {
if (gatheringNumber) {
//console.log( "Force completed.")
gatheringNumber = false;
val.value_type = VALUE_NUMBER;
if (parse_context == CONTEXT_UNKNOWN) {
completed = true;
} else {
throw new Error("context stack is not empty at flush");
}
retval = 1; // if returning buffers, then obviously there's more in this one.
}
}
while (status && (input = inQueue.shift())) {
n = input.n;
buf = input.buf;
if (gatheringString) {
const string_status = gatherString(gatheringStringFirstChar);
if (string_status > 0) {
gatheringString = false;
val.value_type = VALUE_STRING;
}
}
if (gatheringNumber) {
collectNumber();
}
while (!completed && status && n < buf.length) {
let str = buf.charAt(n);
const cInt = buf.codePointAt(n++);
if (cInt >= 0x10000) {
str += buf.charAt(n);
n++;
} //// log('_DEBUG_PARSING', "parsing at ", cInt, str );
//log('_DEBUG_LL', "processing: ", cInt, str, pos, comment, parse_context, word, val );
pos.col++;
if (comment) {
// '/'
if (comment == 1) {
// '/'
if (cInt == 42
/*'*'*/
) {
comment = 3;
} // '/*'
else if (cInt != 47
/*'/'*/
) {
// '//'(NOT)
throwError("fault while parsing;", cInt);
} else comment = 2; // '//' (valid)
} else if (comment == 2) {
// '// ...'
if (cInt == 10
/*'\n'*/
|| cInt == 13
/*'\r'*/
) comment = 0;
} else if (comment == 3) {
// '/*... '
if (cInt == 42
/*'*'*/
) comment = 4;
} else {
// if( comment == 4 ) { // '/* ... *'
if (cInt == 47
/*'/'*/
) comment = 0;else comment = 3; // any other char, goto expect * to close */
}
continue;
}
switch (cInt) {
case 47
/*'/'*/
:
comment = 1;
break;
case 123
/*'{'*/
:
if (word == WORD_POS_FIELD || word == WORD_POS_AFTER_FIELD || parse_context == CONTEXT_OBJECT_FIELD && word == WORD_POS_RESET) {
throwError("fault while parsing; getting field name unexpected ", cInt); // break;
}
{
const old_context = getContext(); //log('_DEBUG_PARSING', "Begin a new object; previously pushed into elements; but wait until trailing comma or close previously:%d", val.value_type );
val.value_type = VALUE_OBJECT;
const tmpobj = {};
if (parse_context == CONTEXT_UNKNOWN) result = elements = tmpobj;
old_context.context = parse_context;
old_context.elements = elements;
old_context.element_array = element_array;
old_context.name = val.name;
elements = tmpobj; //log('_DEBUG_PARSING_STACK',"push context (open object): ", context_stack.length );
context_stack.push(old_context);
RESET_VAL();
parse_context = CONTEXT_OBJECT_FIELD;
}
break;
case 91
/*'['*/
:
if (parse_context == CONTEXT_OBJECT_FIELD || word == WORD_POS_FIELD || word == WORD_POS_AFTER_FIELD) {
throwError("Fault while parsing; while getting field name unexpected", cInt); // break;
}
if (val.value_type == VALUE_UNSET || val.value_type == VALUE_UNDEFINED) {
const old_context = getContext(); //log('_DEBUG_PARSING', "Begin a new array; previously pushed into elements; but wait until trailing comma or close previously:%d", val.value_type );
val.value_type = VALUE_ARRAY;
const tmparr = [];
if (parse_context == CONTEXT_UNKNOWN) result = element_array = tmparr; //else if( parse_context == CONTEXT_IN_ARRAY )
// element_array.push( tmparr );
else if (parse_context == CONTEXT_OBJECT_FIELD_VALUE) elements[val.name] = tmparr;
old_context.context = parse_context;
old_context.elements = elements;
old_context.element_array = element_array;
old_context.name = val.name;
element_array = tmparr; //log('_DEBUG_PARSING_STACK', "push context (open array): ", context_stack.length );
context_stack.push(old_context);
RESET_VAL();
parse_context = CONTEXT_IN_ARRAY;
} else {
throwError("Unexpected array open after previous value", cInt);
}
break;
case 58
/*':'*/
:
////log('_DEBUG_PARSING', "colon context:", parse_context );
if (parse_context == CONTEXT_OBJECT_FIELD) {
word = WORD_POS_RESET;
val.name = val.string;
val.string = '';
parse_context = CONTEXT_OBJECT_FIELD_VALUE;
val.value_type = VALUE_UNSET;
} else {
if (parse_context == CONTEXT_IN_ARRAY) throwError("(in array, got colon out of string):parsing fault;", cInt);else throwError("(outside any object, got colon out of string):parsing fault;", cInt);
}
break;
case 125
/*'}'*/
:
////log('_DEBUG_PARSING', "close bracket context:", word, parse_context );
if (word == WORD_POS_END) {
// allow starting a new word
word = WORD_POS_RESET;
} // coming back after pushing an array or sub-object will reset the context to FIELD, so an end with a field should still push value.
if (parse_context == CONTEXT_OBJECT_FIELD) {
//log('_DEBUG_PARSING', "close object; empty object %d", val.value_type );
//RESET_VAL();
val.value_type = VALUE_OBJECT;
val.contains = elements;
const old_context = context_stack.pop(); //log('_DEBUG_PARSING_STACK',"object pop stack (close obj)", context_stack.length, old_context );
val.name = old_context.name;
parse_context = old_context.context; // this will restore as IN_ARRAY or OBJECT_FIELD
elements = old_context.elements;
element_array = old_context.element_array;
dropContext(old_context);
if (parse_context == CONTEXT_UNKNOWN) {
completed = true;
}
} else if (parse_context == CONTEXT_OBJECT_FIELD_VALUE) {
// first, add the last value
//log('_DEBUG_PARSING', "close object; push item '%s' %d", val.name, val.value_type );
if (val.value_type != VALUE_UNSET) {
objectPush();
} else {
throwError("Fault while parsing field value, close with no value", cInt);
}
val.value_type = VALUE_OBJECT;
val.contains = elements;
const old_context = context_stack.pop(); //log('_DEBUG_PARSING_STACK',"object pop stack (close object)", context_stack.length, old_context );
val.name = old_context.name;
parse_context = old_context.context; // this will restore as IN_ARRAY or OBJECT_FIELD
elements = old_context.elements;
element_array = old_context.element_array;
dropContext(old_context);
if (parse_context == CONTEXT_UNKNOWN) {
completed = true;
}
} else {
throwError("Fault while parsing; unexpected", cInt);
}
negative = false;
break;
case 93
/*']'*/
:
if (word == WORD_POS_END) word = WORD_POS_RESET;
if (parse_context == CONTEXT_IN_ARRAY) {
//log('_DEBUG_PARSING', "close array, push last element: %d", val.value_type );
if (val.value_type != VALUE_UNSET) {
arrayPush();
}
val.value_type = VALUE_ARRAY;
val.contains = element_array;
{
const old_context = context_stack.pop(); //log('_DEBUG_PARSING_STACK',"object pop stack (close array)", context_stack.length );
val.name = old_context.name;
parse_context = old_context.context;
elements = old_context.elements;
element_array = old_context.element_array;
dropContext(old_context);
}
if (parse_context == CONTEXT_UNKNOWN) {
completed = true;
}
} else {
throwError(`bad context ${parse_context}; fault while parsing`, cInt); // fault
}
negative = false;
break;
case 44
/*','*/
:
if (word == WORD_POS_END) word = WORD_POS_RESET; // allow collect new keyword
//log('_DEBUG_PARSING', "comma context:", parse_context, val );
if (parse_context == CONTEXT_IN_ARRAY) {
if (val.value_type == VALUE_UNSET) val.value_type = VALUE_EMPTY; // in an array, elements after a comma should init as undefined...
//log('_DEBUG_PARSING', "back in array; push item %d", val.value_type );
arrayPush();
RESET_VAL(); // undefined allows [,,,] to be 4 values and [1,2,3,] to be 4 values with an undefined at end.
} else if (parse_context == CONTEXT_OBJECT_FIELD_VALUE) {
// after an array value, it will have returned to OBJECT_FIELD anyway
//log('_DEBUG_PARSING', "comma after field value, push field to object: %s", val.name );
parse_context = CONTEXT_OBJECT_FIELD;
if (val.value_type != VALUE_UNSET) {
objectPush();
RESET_VAL();
} else throwError("Unexpected comma after object field name", cInt);
} else {
status = false;
throwError("bad context; excessive commas while parsing;", cInt); // fault
}
negative = false;
break;
default:
if (parse_context == CONTEXT_OBJECT_FIELD) {
switch (cInt) {
case 96: //'`':
case 34: //'"':
case 39:
//'\'':
if (word == WORD_POS_RESET) {
if (val.value_type != VALUE_UNSET) throwError("String begin after previous value", cInt);
const string_status = gatherString(cInt); //log('_DEBUG_PARSING', "string gather for object field name :", val.string, string_status );
if (string_status) {
val.value_type = VALUE_STRING;
} else {
gatheringStringFirstChar = cInt;
gatheringString = true;
}
} else {
throwError("fault while parsing; quote not at start of field name", cInt);
}
break;
case 10:
//'\n':
pos.line++;
pos.col = 1;
// fall through to normal space handling - just updated line/col position
case 13: //'\r':
case 32: //' ':
case 160: // :
case 9: //'\t':
case 0xFEFF:
// ZWNBS is WS though
if (word == WORD_POS_END) {
// allow collect new keyword
word = WORD_POS_RESET;
} else if (word == WORD_POS_FIELD) {
word = WORD_POS_AFTER_FIELD;
} // skip whitespace
break;
default:
if (word == WORD_POS_AFTER_FIELD) {
status = false;
throwError("fault while parsing; character unexpected", cInt);
}
if (word == WORD_POS_RESET) word = WORD_POS_FIELD;
val.string += str;
break;
// default
}
} else switch (cInt) {
case 96: //'`':
case 34: //'"':
case 39:
{
//'\'':
if (val.value_type === VALUE_UNSET) {
const string_status = gatherString(cInt); //log('_DEBUG_PARSING', "string gather for object field value :", val.string, string_status, completed, input.n, buf.length );
if (string_status) {
val.value_type = VALUE_STRING;
word = WORD_POS_END;
} else {
gatheringStringFirstChar = cInt;
gatheringString = true;
}
} else throwError("String unexpected", cInt);
break;
}
case 10:
//'\n':
pos.line++;
pos.col = 1;
// Fallthrough
case 32: //' ':
case 160: //  
case 9: //'\t':
case 13: //'\r':
case 0xFEFF:
//'\uFEFF':
if (word == WORD_POS_END) {
word = WORD_POS_RESET;
if (parse_context == CONTEXT_UNKNOWN) {
completed = true;
}
break;
}
if (word !== WORD_POS_RESET) {
// breaking in the middle of gathering a keyword.
status = false;
throwError("fault parsing whitespace", cInt);
}
break;
//----------------------------------------------------------
// catch characters for true/false/null/undefined which are values outside of quotes
case 116:
//'t':
if (word == WORD_POS_RESET) word = WORD_POS_TRUE_1;else if (word == WORD_POS_INFINITY_6) word = WORD_POS_INFINITY_7;else {
status = false;
throwError("fault parsing", cInt);
} // fault
break;
case 114:
//'r':
if (word == WORD_POS_TRUE_1) word = WORD_POS_TRUE_2;else {
status = false;
throwError("fault parsing", cInt);
} // fault
break;
case 117:
//'u':
if (word == WORD_POS_TRUE_2) word = WORD_POS_TRUE_3;else if (word == WORD_POS_NULL_1) word = WORD_POS_NULL_2;else if (word == WORD_POS_RESET) word = WORD_POS_UNDEFINED_1;else {
status = false;
throwError("fault parsing", cInt);
} // fault
break;
case 101:
//'e':
if (word == WORD_POS_TRUE_3) {
val.value_type = VALUE_TRUE;
word = WORD_POS_END;
} else if (word == WORD_POS_FALSE_4) {
val.value_type = VALUE_FALSE;
word = WORD_POS_END;
} else if (word == WORD_POS_UNDEFINED_3) word = WORD_POS_UNDEFINED_4;else if (word == WORD_POS_UNDEFINED_7) word = WORD_POS_UNDEFINED_8;else {
status = false;
throwError("fault parsing", cInt);
} // fault
break;
case 110:
//'n':
if (word == WORD_POS_RESET) word = WORD_POS_NULL_1;else if (word == WORD_POS_UNDEFINED_1) word = WORD_POS_UNDEFINED_2;else if (word == WORD_POS_UNDEFINED_6) word = WORD_POS_UNDEFINED_7;else if (word == WORD_POS_INFINITY_1) word = WORD_POS_INFINITY_2;else if (word == WORD_POS_INFINITY_4) word = WORD_POS_INFINITY_5;else {
status = false;
throwError("fault parsing", cInt);
} // fault
break;
case 100:
//'d':
if (word == WORD_POS_UNDEFINED_2) word = WORD_POS_UNDEFINED_3;else if (word == WORD_POS_UNDEFINED_8) {
val.value_type = VALUE_UNDEFINED;
word = WORD_POS_END;
} else {
status = false;
throwError("fault parsing", cInt);
} // fault
break;
case 105:
//'i':
if (word == WORD_POS_UNDEFINED_5) word = WORD_POS_UNDEFINED_6;else if (word == WORD_POS_INFINITY_3) word = WORD_POS_INFINITY_4;else if (word == WORD_POS_INFINITY_5) word = WORD_POS_INFINITY_6;else {
status = false;
throwError("fault parsing", cInt);
} // fault
break;
case 108:
//'l':
if (word == WORD_POS_NULL_2) word = WORD_POS_NULL_3;else if (word == WORD_POS_NULL_3) {
val.value_type = VALUE_NULL;
word = WORD_POS_END;
} else if (word == WORD_POS_FALSE_2) word = WORD_POS_FALSE_3;else {
status = false;
throwError("fault parsing", cInt);
} // fault
break;
case 102:
//'f':
if (word == WORD_POS_RESET) word = WORD_POS_FALSE_1;else if (word == WORD_POS_UNDEFINED_4) word = WORD_POS_UNDEFINED_5;else if (word == WORD_POS_INFINITY_2) word = WORD_POS_INFINITY_3;else {
status = false;
throwError("fault parsing", cInt);
} // fault
break;
case 97:
//'a':
if (word == WORD_POS_FALSE_1) word = WORD_POS_FALSE_2;else if (word == WORD_POS_NAN_1) word = WORD_POS_NAN_2;else {
status = false;
throwError("fault parsing", cInt);
} // fault
break;
case 115:
//'s':
if (word == WORD_POS_FALSE_3) word = WORD_POS_FALSE_4;else {
status = false;
throwError("fault parsing", cInt);
} // fault
break;
case 73:
//'I':
if (word == WORD_POS_RESET) word = WORD_POS_INFINITY_1;else {
status = false;
throwError("fault parsing", cInt);
} // fault
break;
case 78:
//'N':
if (word == WORD_POS_RESET) word = WORD_POS_NAN_1;else if (word == WORD_POS_NAN_2) {
val.value_type = negative ? VALUE_NEG_NAN : VALUE_NAN;
negative = false;
word = WORD_POS_END;
} else {
status = false;
throwError("fault parsing", cInt);
} // fault
break;
case 121:
//'y':
if (word == WORD_POS_INFINITY_7) {
val.value_type = negative ? VALUE_NEG_INFINITY : VALUE_INFINITY;
negative = false;
word = WORD_POS_END;
} else {
status = false;
throwError("fault parsing", cInt);
} // fault
break;
case 45:
//'-':
if (word == WORD_POS_RESET) negative = !negative;else {
status = false;
throwError("fault parsing", cInt);
} // fault
break;
case 43:
//'+':
if (word !== WORD_POS_RESET) {
status = false;
throwError("fault parsing", cInt);
} // fault
break;
//
//----------------------------------------------------------
default:
if (cInt >= 48
/*'0'*/
&& cInt <= 57
/*'9'*/
|| cInt == 43
/*'+'*/
|| cInt == 46
/*'.'*/
|| cInt == 45
/*'-'*/
) {
fromHex = false;
exponent = false;
exponent_sign = false;
exponent_digit = false;
decimal = false;
val.string = str;
input.n = n;
collectNumber();
} else {
status = false;
throwError("fault parsing", cInt);
}
break;
// default
}
break;
// default of high level switch
}
if (completed) {
if (word == WORD_POS_END) {
word = WORD_POS_RESET;
}
break;
}
}
if (n == buf.length) {
dropBuffer(input);
if (gatheringString || gatheringNumber || parse_context == CONTEXT_OBJECT_FIELD) {
retval = 0;
} else {
if (parse_context == CONTEXT_UNKNOWN && (val.value_type != VALUE_UNSET || result)) {
completed = true;
retval = 1;
}
}
} else {
// put these back into the stack.
input.n = n;
inQueue.unshift(input);
retval = 2; // if returning buffers, then obviously there's more in this one.
}
if (completed) break;
}
if (completed && val.value_type != VALUE_UNSET) {
switch (val.value_type) {
case VALUE_NUMBER:
result = (negative ? -1 : 1) * Number(val.string);
break;
case VALUE_STRING:
result = val.string;
break;
case VALUE_TRUE:
result = true;
break;
case VALUE_FALSE:
result = false;
break;
case VALUE_NULL:
result = null;
break;
case VALUE_UNDEFINED:
result = undefined;
break;
case VALUE_NAN:
result = NaN;
break;
case VALUE_NEG_NAN:
result = -NaN;
break;
case VALUE_INFINITY:
result = Infinity;
break;
case VALUE_NEG_INFINITY:
result = -Infinity;
break;
case VALUE_OBJECT:
// never happens
result = val.contains;
break;
case VALUE_ARRAY:
// never happens
result = val.contains;
break;
}
negative = false;
val.string = '';
val.value_type = VALUE_UNSET;
}
completed = false;
return retval;
}
};
};
const _parser = [Object.freeze(JSON6.begin())];
let _parse_level = 0;
JSON6.parse = function (msg, reviver) {
//var parser = JSON6.begin();
const parse_level = _parse_level++;
if (_parser.length <= parse_level) _parser.push(Object.freeze(JSON6.begin()));
const parser = _parser[parse_level];
if (typeof msg !== "string") msg = String(msg);
parser.reset();
if (parser._write(msg, true) > 0) {
const result = parser.value();
if (typeof reviver === 'function') (function walk(holder, key) {
const value = holder[key];
if (value && typeof value === 'object') {
for (const k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
const v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
})({
'': result
}, '');
_parse_level--;
return result;
} else parser.finalError();
return undefined;
};
JSON6.stringify = JSON.stringify; //---------------------------------------------------------------------------
// Stringify
//---------------------------------------------------------------------------
JSON6.stringifierActive = null;
JSON6.stringifier = function () {
const keywords = {
["true"]: true,
["false"]: false,
["null"]: null,
["NaN"]: NaN,
["Infinity"]: Infinity,
["undefined"]: undefined
};
let useQuote =