simple-object-stream
Version:
A Transform stream which parses json from plaintext input.
237 lines (225 loc) • 9.89 kB
JavaScript
// Generated by CoffeeScript 1.9.2
(function() {
var SimpleObjectStream, Transform, util,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
util = require('util');
Transform = require('stream').Transform;
module.exports = SimpleObjectStream = (function(superClass) {
extend(SimpleObjectStream, superClass);
function SimpleObjectStream() {
var cbError;
if (!this instanceof SimpleObjectStream) {
return new SimpleObjectStream;
} else {
Transform.call(this, {
readableObjectMode: true
});
this.delimiterStack = [];
this.curObjArr = [];
this.prevChar = "";
this.inString = false;
this.curKey = [];
this.curVal = [];
this.isKey = false;
this.buffer = "";
cbError = (function(_this) {
return function(err) {
return _this.emit('error', err);
};
})(this);
this.on('pipe', (function(_this) {
return function(src) {
return src.on('error', cbError);
};
})(this));
this.on('unpipe', (function(_this) {
return function(src) {
return src.removeListener('error', cbError);
};
})(this));
}
}
SimpleObjectStream.prototype._flush = function(callback) {
this.pushObjs(this.buffer);
if (this.buffer.match(/[^\s]/g)) {
this.emit('error', new Error("Unparsable end of stream."));
}
return typeof callback === "function" ? callback() : void 0;
};
SimpleObjectStream.prototype._transform = function(chunk, encoding, callback) {
var str;
str = this.buffer + chunk.toString();
this.pushObjs(str);
return typeof callback === "function" ? callback() : void 0;
};
SimpleObjectStream.prototype.pushObjs = function(str) {
var c, err, finalObj, i, j, prevDelim, ref, results;
results = [];
for (i = j = 0, ref = str.length - 1; j <= ref; i = j += 1) {
c = str.charAt(i);
if (this.inString) {
if (this.isKey) {
this.curKey.push(c);
} else {
this.curVal.push(c);
}
if (c === "\"" && this.prevChar !== "\\") {
this.inString = false;
}
results.push(this.prevChar = c);
} else {
if (c === "{" || c === "[") {
if (this.prevChar === "" || this.prevChar === ":") {
this.delimiterStack.push(c);
this.curObjArr.push(c);
this.prevChar = c;
if (c === "{") {
results.push(this.isKey = true);
} else {
results.push(void 0);
}
} else {
results.push(this.emit('error', new Error("input stream not valid json: invalid positioning of '" + c + "' at " + (this.curObjArr.join("")))));
}
} else if (c === "]" || c === "}") {
prevDelim = this.delimiterStack.pop();
if (!this.isKey && this.curVal.join("").match(/(true|false|null|[0-9\.]+|"[^"]*")/)) {
if (prevDelim === "[" && c !== "]" || prevDelim === "{" && c !== "}") {
results.push(this.emit('error', new Error("input stream not valid json: invalid type of closing '" + c + "' at " + (this.curObjArr.join("")))));
} else {
this.curObjArr.push(this.curVal.join(""));
this.curObjArr.push(c);
this.curVal = [];
if (this.delimiterStack.length === 0) {
try {
finalObj = JSON.parse(this.curObjArr.join(""));
this.emit('object', finalObj);
this.push(JSON.stringify(finalObj));
this.buffer = str.substr(0, i + 1);
} catch (_error) {
err = _error;
this.emit('error', err);
}
this.curObjArr = [];
results.push(this.prevChar = "");
} else {
results.push(this.prevChar = c);
}
}
} else {
if (prevDelim === "{" && c === "}") {
this.curObjArr.push(c);
if (this.delimiterStack.length === 0) {
try {
finalObj = JSON.parse(this.curObjArr.join(""));
this.emit('object', finalObj);
this.push(JSON.stringify(finalObj));
} catch (_error) {
err = _error;
this.emit('error', err);
}
this.curObjArr = [];
results.push(this.prevChar = "");
} else {
results.push(this.prevChar = c);
}
} else if (prevDelim === "[" && c === "]") {
this.curObjArr.push(this.curKey.join(""));
this.curObjArr.push(c);
this.curKey = [];
if (this.delimiterStack.length === 0) {
try {
finalObj = JSON.parse(this.curObjArr.join(""));
this.emit('object', finalObj);
this.push(JSON.stringify(finalObj));
} catch (_error) {
err = _error;
this.emit('error', err);
}
this.curObjArr = [];
results.push(this.prevChar = "");
} else {
results.push(this.prevChar = c);
}
} else {
results.push(this.emit('error', new Error("input stream not valid json: invalid positioning of '" + c + "' at " + (this.curObjArr.join("")))));
}
}
} else if (c === ":") {
if (this.delimiterStack[this.delimiterStack.length - 1] === "[") {
results.push(this.emit('error', new Error("input stream not valid json: ':' within array at " + (this.curObjArr.join("")))));
} else if (this.isKey && this.prevChar === "\"") {
this.isKey = false;
this.curObjArr.push(this.curKey.join(""));
this.curKey = [];
this.curObjArr.push(c);
results.push(this.prevChar = c);
} else {
results.push(this.emit('error', new Error("input stream not valid json: invalid positioning of ':' at " + (this.curObjArr.join("")))));
}
} else if (c === ",") {
if (this.delimiterStack[this.delimiterStack.length - 1] === "[" && this.curKey.join("").match(/(true|false|null|[0-9\.]+|"[^"]*")/)) {
this.curObjArr.push(this.curKey.join(""));
this.curKey = [];
this.curObjArr.push(c);
results.push(this.prevChar = c);
} else if (!this.isKey && this.curVal.join("").match(/(true|false|null|[0-9\.]+|"[^"]*")/)) {
this.isKey = true;
this.curObjArr.push(this.curVal.join(""));
this.curVal = [];
this.curObjArr.push(c);
results.push(this.prevChar = c);
} else if (this.prevChar === "]" || this.prevChar === "}") {
this.curObjArr.push(c);
this.curVal = [];
this.curKey = [];
this.isKey = true;
results.push(this.prevChar = c);
} else {
results.push(this.emit('error', new Error("input stream not valid json: invalid positioning of ',' at " + (this.curObjArr.join("")))));
}
} else if (c === "\"") {
if (this.prevChar === ":") {
this.inString = true;
this.curVal.push(c);
results.push(this.prevChar = c);
} else if (this.prevChar === "," || this.prevChar === "{" || this.prevChar === "[") {
this.isKey = true;
this.inString = true;
this.curKey.push(c);
results.push(this.prevChar = c);
} else {
results.push(this.emit('error', new Error("input stream not valid json: invalid positioning of '\"' at " + (this.curObjArr.join("")))));
}
} else if (c.match(/[truefasnl]/)) {
if (!this.isKey) {
this.curVal.push(c);
results.push(this.prevChar = c);
} else if (this.delimiterStack[this.delimiterStack.length - 1] === "[") {
this.curKey.push(c);
results.push(this.prevChar = c);
} else {
results.push(this.emit('error', new Error("input stream not valid json: invalid positioning of 'true/false' at " + (this.curObjArr.join("")))));
}
} else if (c.match(/[0-9\.]/)) {
if (!this.isKey) {
this.curVal.push(c);
results.push(this.prevChar = c);
} else if (this.delimiterStack[this.delimiterStack.length - 1] === "[") {
this.curKey.push(c);
results.push(this.prevChar = c);
} else {
results.push(this.emit('error', new Error("input stream not valid json: invalid positioning of numberic literal at " + (this.curObjArr.join("")))));
}
} else if (c.match(/\s/)) {
} else {
results.push(this.emit('error', new Error("input stream not valid json: invalid char: " + c)));
}
}
}
return results;
};
return SimpleObjectStream;
})(Transform);
}).call(this);