You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
174 lines
4.5 KiB
174 lines
4.5 KiB
'use strict';
|
|
|
|
/**
|
|
* Stringify/parse functions that don't operate
|
|
* recursively, so they avoid call stack exceeded
|
|
* errors.
|
|
*/
|
|
exports.stringify = function stringify(input) {
|
|
var queue = [];
|
|
queue.push({obj: input});
|
|
|
|
var res = '';
|
|
var next, obj, prefix, val, i, arrayPrefix, keys, k, key, value, objPrefix;
|
|
while ((next = queue.pop())) {
|
|
obj = next.obj;
|
|
prefix = next.prefix || '';
|
|
val = next.val || '';
|
|
res += prefix;
|
|
if (val) {
|
|
res += val;
|
|
} else if (typeof obj !== 'object') {
|
|
res += typeof obj === 'undefined' ? null : JSON.stringify(obj);
|
|
} else if (obj === null) {
|
|
res += 'null';
|
|
} else if (Array.isArray(obj)) {
|
|
queue.push({val: ']'});
|
|
for (i = obj.length - 1; i >= 0; i--) {
|
|
arrayPrefix = i === 0 ? '' : ',';
|
|
queue.push({obj: obj[i], prefix: arrayPrefix});
|
|
}
|
|
queue.push({val: '['});
|
|
} else { // object
|
|
keys = [];
|
|
for (k in obj) {
|
|
if (obj.hasOwnProperty(k)) {
|
|
keys.push(k);
|
|
}
|
|
}
|
|
queue.push({val: '}'});
|
|
for (i = keys.length - 1; i >= 0; i--) {
|
|
key = keys[i];
|
|
value = obj[key];
|
|
objPrefix = (i > 0 ? ',' : '');
|
|
objPrefix += JSON.stringify(key) + ':';
|
|
queue.push({obj: value, prefix: objPrefix});
|
|
}
|
|
queue.push({val: '{'});
|
|
}
|
|
}
|
|
return res;
|
|
};
|
|
|
|
// Convenience function for the parse function.
|
|
// This pop function is basically copied from
|
|
// pouchCollate.parseIndexableString
|
|
function pop(obj, stack, metaStack) {
|
|
var lastMetaElement = metaStack[metaStack.length - 1];
|
|
if (obj === lastMetaElement.element) {
|
|
// popping a meta-element, e.g. an object whose value is another object
|
|
metaStack.pop();
|
|
lastMetaElement = metaStack[metaStack.length - 1];
|
|
}
|
|
var element = lastMetaElement.element;
|
|
var lastElementIndex = lastMetaElement.index;
|
|
if (Array.isArray(element)) {
|
|
element.push(obj);
|
|
} else if (lastElementIndex === stack.length - 2) { // obj with key+value
|
|
var key = stack.pop();
|
|
element[key] = obj;
|
|
} else {
|
|
stack.push(obj); // obj with key only
|
|
}
|
|
}
|
|
|
|
exports.parse = function (str) {
|
|
var stack = [];
|
|
var metaStack = []; // stack for arrays and objects
|
|
var i = 0;
|
|
var collationIndex,parsedNum,numChar;
|
|
var parsedString,lastCh,numConsecutiveSlashes,ch;
|
|
var arrayElement, objElement;
|
|
while (true) {
|
|
collationIndex = str[i++];
|
|
if (collationIndex === '}' ||
|
|
collationIndex === ']' ||
|
|
typeof collationIndex === 'undefined') {
|
|
if (stack.length === 1) {
|
|
return stack.pop();
|
|
} else {
|
|
pop(stack.pop(), stack, metaStack);
|
|
continue;
|
|
}
|
|
}
|
|
switch (collationIndex) {
|
|
case ' ':
|
|
case '\t':
|
|
case '\n':
|
|
case ':':
|
|
case ',':
|
|
break;
|
|
case 'n':
|
|
i += 3; // 'ull'
|
|
pop(null, stack, metaStack);
|
|
break;
|
|
case 't':
|
|
i += 3; // 'rue'
|
|
pop(true, stack, metaStack);
|
|
break;
|
|
case 'f':
|
|
i += 4; // 'alse'
|
|
pop(false, stack, metaStack);
|
|
break;
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
case '-':
|
|
parsedNum = '';
|
|
i--;
|
|
while (true) {
|
|
numChar = str[i++];
|
|
if (/[\d\.\-e\+]/.test(numChar)) {
|
|
parsedNum += numChar;
|
|
} else {
|
|
i--;
|
|
break;
|
|
}
|
|
}
|
|
pop(parseFloat(parsedNum), stack, metaStack);
|
|
break;
|
|
case '"':
|
|
parsedString = '';
|
|
lastCh = void 0;
|
|
numConsecutiveSlashes = 0;
|
|
while (true) {
|
|
ch = str[i++];
|
|
if (ch !== '"' || (lastCh === '\\' &&
|
|
numConsecutiveSlashes % 2 === 1)) {
|
|
parsedString += ch;
|
|
lastCh = ch;
|
|
if (lastCh === '\\') {
|
|
numConsecutiveSlashes++;
|
|
} else {
|
|
numConsecutiveSlashes = 0;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
pop(JSON.parse('"' + parsedString + '"'), stack, metaStack);
|
|
break;
|
|
case '[':
|
|
arrayElement = { element: [], index: stack.length };
|
|
stack.push(arrayElement.element);
|
|
metaStack.push(arrayElement);
|
|
break;
|
|
case '{':
|
|
objElement = { element: {}, index: stack.length };
|
|
stack.push(objElement.element);
|
|
metaStack.push(objElement);
|
|
break;
|
|
default:
|
|
throw new Error(
|
|
'unexpectedly reached end of input: ' + collationIndex);
|
|
}
|
|
}
|
|
};
|