jsonast
Version:
a json to ast parser which allows error recovery
251 lines • 5.52 kB
JavaScript
;
var character_stream_1 = require('./character-stream');
/**
* Parses a given string into a Json AST.
* This parser does some error correction (notably missing comma in objects and arrays).
* The template parameter could be used to qualify the result AST.
*
* @export
* @param {string} text The Json text to parse
* @returns {JsonObject|JsonArray} Either a Json Object or Json Array AST node
*/
function parse(text) {
var result = undefined;
var cs = new character_stream_1.CharacterStream(text);
ws(cs);
if (cs.ch === '{') {
result = object(cs);
}
else if (cs.ch === '[') {
result = array(cs);
}
ws(cs);
if (!cs.eoi) {
throw new Error("Unexpected character '" + cs.ch + "' at " + cs.line + ":" + cs.column + ". Expected end of input.");
}
return result;
}
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = parse;
function object(cs) {
function members(cs1) {
function pair(cs2) {
ws(cs2);
var key = string(cs2);
ws(cs2);
cs2.accept(':');
return {
key: key,
value: value(cs2)
};
}
var members = [];
var next = true;
while (next) {
members.push(pair(cs1));
ws(cs1);
if (cs1.ch === ',') {
cs1.next();
}
else if (cs1.ch === '"') {
}
else {
next = false;
}
}
return members;
}
var ast = {
type: 'object',
pos: {
start: cs.pos,
end: cs.pos
}
};
ws(cs);
cs.accept('{');
ws(cs);
if (cs.ch === '"') {
ast.members = members(cs);
}
ws(cs);
cs.accept('}');
ast.pos.end = cs.pos;
return ast;
}
function array(cs) {
var ast = {
type: 'array',
pos: {
start: cs.pos,
end: cs.pos
}
};
ws(cs);
cs.accept('[');
if (cs.ch !== ']') {
ast.elements = [];
var next = true;
while (next) {
ast.elements.push(value(cs));
ws(cs);
if (cs.ch === ',') {
cs.next();
}
else if (cs.ch !== ']') {
}
else {
next = false;
}
}
}
ws(cs);
cs.accept(']');
ast.pos.end = cs.pos;
return ast;
}
function value(cs) {
ws(cs);
switch (cs.ch) {
case '"':
return string(cs);
case '{':
return object(cs);
case '[':
return array(cs);
case 't':
return trueLiteral(cs);
case 'f':
return falseLiteral(cs);
case 'n':
return nullLiteral(cs);
default:
return number(cs);
}
}
function string(cs) {
var start = cs.pos;
var value = '';
cs.accept('"');
while (cs.ch !== '"') {
value += cs.ch;
cs.next();
}
cs.accept('"');
return {
type: 'string',
value: value,
pos: {
start: start,
end: cs.pos
}
};
}
function trueLiteral(cs) {
ws(cs);
var start = cs.pos;
cs.accept('t');
cs.accept('r');
cs.accept('u');
cs.accept('e');
return {
type: 'true',
pos: {
start: start,
end: cs.pos
}
};
}
function falseLiteral(cs) {
ws(cs);
var start = cs.pos;
cs.accept('f');
cs.accept('a');
cs.accept('l');
cs.accept('s');
cs.accept('e');
return {
type: 'false',
pos: {
start: start,
end: cs.pos
}
};
}
function nullLiteral(cs) {
ws(cs);
var start = cs.pos;
cs.accept('n');
cs.accept('u');
cs.accept('l');
cs.accept('l');
return {
type: 'null',
pos: {
start: start,
end: cs.pos
}
};
}
function number(cs) {
function digit() {
var number = '';
var ch = cs.ch;
if (ch === '0' || ch === '1' || ch === '2' || ch === '3' || ch === '4' ||
ch === '5' || ch === '6' || ch === '7' || ch === '8' || ch === '9') {
number = ch;
cs.next();
}
return number;
}
function digits() {
var number = digit();
var temp = digit();
while (temp !== '') {
number += temp;
temp = digit();
}
return number;
}
var start = cs.pos;
var negative = cs.skip('-') ? '-' : '';
var int;
if (cs.ch === '0') {
int = cs.ch;
cs.next();
}
else {
int = digits();
}
var frac = '';
if (cs.ch === '.') {
cs.next();
frac = '.' + digits();
}
var exp = '';
if (cs.ch === 'e' || cs.ch === 'E') {
cs.next();
exp = 'e';
exp += cs.skip('+') ? '+' : '';
exp += cs.skip('-') ? '-' : '';
exp += digits();
}
return {
type: 'number',
value: parseFloat("" + negative + int + frac + exp),
pos: {
start: start,
end: cs.pos
}
};
}
function ws(cs) {
var next = true;
while (next) {
next = cs.skip(' ') ||
cs.skip('\t') ||
cs.skip('\n') ||
cs.skip('\r');
}
}
//# sourceMappingURL=index.js.map