dummy-streaming-array-parser
Version:
Dummy Parser for streaming JSON as actual JSON Array
125 lines (107 loc) • 3.04 kB
JavaScript
var stream = require('stream');
var util = require('util');
var Tokenizer = require('tokenizer');
var combine = require('stream-combiner');
function JsonTokenizer() {
var t = new Tokenizer();
t.addRule(/^,$/, 'comma');
t.addRule(/^:$/, 'end-label');
t.addRule(/^\{$/, 'begin-object');
t.addRule(/^\}$/, 'end-object');
t.addRule(/^\[$/, 'begin-array');
t.addRule(/^\]$/, 'end-array');
t.addRule(/^"([^"]|\\")*"$/, 'string');
t.addRule(/^"([^"]|\\")*$/, 'maybe-string');
t.addRule(/^null$/, 'null');
t.addRule(/^(true|false)$/, 'boolean');
t.addRule(/^-?\d+(\.\d+)?([eE]-?\d+)?$/, 'number');
t.addRule(/^-?\d+\.$/, 'maybe-decimal-number');
t.addRule(/^-$/, 'maybe-negative-number');
t.addRule(/^-?\d+(\.\d+)?([eE])?$/, 'maybe-exponential-number');
t.addRule(/^-?\d+(\.\d+)?([eE]-)?$/, 'maybe-exponential-number-negative');
t.addRule(/^\w+$/, 'symbol');
t.addRule(Tokenizer.whitespace);
t.ignore('whitespace');
// if we had comments tokens, we would ignore them as well
// but the JSON spec doesn't allow comments!
return t;
}
function Parser (options) {
if(!(this instanceof Parser)) {
return new Parser();
}
options = options || {};
options.objectMode = true;
stream.Transform.call(this, options);
this._buffer = [];
this._currentDepth = 0;
}
util.inherits(Parser, stream.Transform);
module.exports = Parser;
Parser.prototype._transform = function _transform(chunk, encoding, callback) {
if(this._currentDepth === 0) {
if (chunk.type === 'begin-array') {
this._currentDepth = 1;
return callback();
}
else {
return callback(new Error('Unexpected token '+chunk+' ('+chunk.type+')'));
}
}
if(this._currentDepth === 1 && chunk.type === 'end-array') {
try {
this._processLine();
} catch(e) {
return callback(e);
}
this._currentDepth = 0;
return callback();
}
switch(chunk.type) {
case 'begin-array':
case 'begin-object':
this._currentDepth++;
break;
case 'end-array':
case 'end-object':
this._currentDepth--;
break;
case 'comma':
if(this._currentDepth === 1) {
chunk = '';
try {
this._processLine();
} catch(e) {
return callback(e);
}
}
break;
default:
break;
}
this._buffer.push(chunk);
callback();
};
Parser.prototype._flush = function _flush(callback) {
if(this._currentDepth > 0) {
return callback(new SyntaxError('Unexpected end of input'));
}
callback();
};
Parser.prototype._processLine = function _processLine() {
if(!this._buffer.length) {
return
}
var json = this._buffer.join('');
if(!json.length && this._currentDepth === 1) {
throw new SyntaxError('Unexpected token ,')
}
this.push(JSON.parse(json));
this._buffer = [];
};
module.exports = function () {
return combine(
new JsonTokenizer(),
new Parser()
);
}