@anchan828/json-ast
Version:
JSON parser AST utilities
319 lines (318 loc) • 14.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parse = parse;
var ast_js_1 = require("./ast.js");
var error_js_1 = require("./error.js");
var junker_js_1 = require("./junker.js");
var parseErrorTypes_js_1 = require("./parseErrorTypes.js");
var position_js_1 = require("./position.js");
var tokenize_js_1 = require("./tokenize.js");
var types_js_1 = require("./types.js");
// import util from 'util';
var objectStates = {
_START_: 0,
OPEN_OBJECT: 1,
KEY: 2,
COLON: 3,
VALUE: 4,
COMMA: 5,
};
var arrayStates = {
_START_: 0,
OPEN_ARRAY: 1,
VALUE: 2,
COMMA: 3,
};
var defaultSettings = {
verbose: true,
junker: false,
};
function parseValue(source, tokenList, index, settings) {
// value: object | array | STRING | NUMBER | TRUE | FALSE | NULL | COMMENT
var token = tokenList[index];
var nodeType;
switch (token.type) {
case types_js_1.JsonTokenTypes.STRING:
nodeType = ast_js_1.JsonNodeTypes.STRING;
break;
case types_js_1.JsonTokenTypes.NUMBER:
nodeType = ast_js_1.JsonNodeTypes.NUMBER;
break;
case types_js_1.JsonTokenTypes.TRUE:
nodeType = ast_js_1.JsonNodeTypes.TRUE;
break;
case types_js_1.JsonTokenTypes.FALSE:
nodeType = ast_js_1.JsonNodeTypes.FALSE;
break;
case types_js_1.JsonTokenTypes.NULL:
nodeType = ast_js_1.JsonNodeTypes.NULL;
break;
case types_js_1.JsonTokenTypes.COMMENT:
nodeType = ast_js_1.JsonNodeTypes.COMMENT;
break;
default:
break;
}
if (nodeType) {
index++;
var value = (nodeType === ast_js_1.JsonNodeTypes.STRING
? ast_js_1.NodeFactory.fromType(nodeType, token.value, token.decoded)
: ast_js_1.NodeFactory.fromType(nodeType, token.value));
if (settings.verbose) {
value.position = token.position;
}
return { value: value, index: index };
}
else {
var objectOrValue = parseObject(source, tokenList, index, settings) || parseArray(source, tokenList, index, settings);
if (objectOrValue) {
return objectOrValue;
}
else {
(0, error_js_1.error)((0, parseErrorTypes_js_1.unexpectedToken)(source.substring(token.position.start.char, token.position.end.char), token.position.start.line, token.position.start.column), source, token.position.start.line, token.position.start.column);
}
}
}
function parseObject(source, tokenList, index, settings) {
var object = ast_js_1.NodeFactory.fromType(ast_js_1.JsonNodeTypes.OBJECT);
var startToken;
var token;
var property;
var state = objectStates._START_;
while (index < tokenList.length) {
token = tokenList[index];
if (token.type === types_js_1.JsonTokenTypes.COMMENT) {
var comment = ast_js_1.NodeFactory.fromType(ast_js_1.JsonNodeTypes.COMMENT, token.value);
if (settings.verbose) {
comment.position = token.position;
}
object.comments.push(comment);
index++;
continue;
}
switch (state) {
case objectStates._START_: {
if (token.type === types_js_1.JsonTokenTypes.LEFT_BRACE) {
startToken = token;
state = objectStates.OPEN_OBJECT;
index++;
}
else {
return null;
}
break;
}
case objectStates.OPEN_OBJECT: {
if (token.type === types_js_1.JsonTokenTypes.STRING) {
property = ast_js_1.NodeFactory.fromType(ast_js_1.JsonNodeTypes.PROPERTY);
property.key = ast_js_1.NodeFactory.fromType(ast_js_1.JsonNodeTypes.KEY, token.value, token.decoded);
if (settings.verbose) {
property.key.position = token.position;
}
state = objectStates.KEY;
index++;
}
else if (token.type === types_js_1.JsonTokenTypes.RIGHT_BRACE) {
if (settings.verbose) {
object.position = new position_js_1.JsonPosition(startToken.position.start.line, startToken.position.start.column, startToken.position.start.char, token.position.end.line, token.position.end.column, token.position.end.char);
}
index++;
return { value: object, index: index };
}
else {
(0, error_js_1.error)((0, parseErrorTypes_js_1.unexpectedToken)(source.substring(token.position.start.char, token.position.end.char), token.position.start.line, token.position.start.column), source, token.position.start.line, token.position.start.column);
}
break;
}
case objectStates.KEY: {
if (token.type === types_js_1.JsonTokenTypes.COLON) {
state = objectStates.COLON;
index++;
}
else {
(0, error_js_1.error)((0, parseErrorTypes_js_1.unexpectedToken)(source.substring(token.position.start.char, token.position.end.char), token.position.start.line, token.position.start.column), source, token.position.start.line, token.position.start.column);
}
break;
}
case objectStates.COLON: {
var value = parseValue(source, tokenList, index, settings);
index = value.index;
property.value = value.value;
object.properties.push(property);
state = objectStates.VALUE;
break;
}
case objectStates.VALUE: {
if (token.type === types_js_1.JsonTokenTypes.RIGHT_BRACE) {
if (settings.verbose) {
object.position = new position_js_1.JsonPosition(startToken.position.start.line, startToken.position.start.column, startToken.position.start.char, token.position.end.line, token.position.end.column, token.position.end.char);
}
index++;
return { value: object, index: index };
}
else if (token.type === types_js_1.JsonTokenTypes.COMMA) {
state = objectStates.COMMA;
index++;
}
else {
(0, error_js_1.error)((0, parseErrorTypes_js_1.unexpectedToken)(source.substring(token.position.start.char, token.position.end.char), token.position.start.line, token.position.start.column), source, token.position.start.line, token.position.start.column);
}
break;
}
case objectStates.COMMA: {
if (token.type === types_js_1.JsonTokenTypes.STRING) {
property = ast_js_1.NodeFactory.fromType(ast_js_1.JsonNodeTypes.PROPERTY);
property.key = ast_js_1.NodeFactory.fromType(ast_js_1.JsonNodeTypes.KEY, token.value, token.decoded);
if (settings.verbose) {
property.key.position = token.position;
}
state = objectStates.KEY;
index++;
}
else if (token.type === types_js_1.JsonTokenTypes.COMMA || token.type === types_js_1.JsonTokenTypes.RIGHT_BRACE) {
// Allow trailing commas
state = objectStates.VALUE;
// index++;
continue;
}
else {
(0, error_js_1.error)((0, parseErrorTypes_js_1.unexpectedToken)(source.substring(token.position.start.char, token.position.end.char), token.position.start.line, token.position.start.column), source, token.position.start.line, token.position.start.column);
}
break;
}
}
}
(0, error_js_1.error)((0, parseErrorTypes_js_1.unexpectedEnd)());
}
function parseArray(source, tokenList, index, settings) {
var array = ast_js_1.NodeFactory.fromType(ast_js_1.JsonNodeTypes.ARRAY);
var startToken;
var token;
var state = arrayStates._START_;
while (index < tokenList.length) {
token = tokenList[index];
if (token.type === types_js_1.JsonTokenTypes.COMMENT) {
var comment = ast_js_1.NodeFactory.fromType(ast_js_1.JsonNodeTypes.COMMENT, token.value);
if (settings.verbose) {
comment.position = token.position;
}
array.comments.push(comment);
index++;
continue;
}
switch (state) {
case arrayStates._START_: {
if (token.type === types_js_1.JsonTokenTypes.LEFT_BRACKET) {
startToken = token;
state = arrayStates.OPEN_ARRAY;
index++;
}
else {
return null;
}
break;
}
case arrayStates.OPEN_ARRAY: {
if (token.type === types_js_1.JsonTokenTypes.RIGHT_BRACKET) {
if (settings.verbose) {
array.position = new position_js_1.JsonPosition(startToken.position.start.line, startToken.position.start.column, startToken.position.start.char, token.position.end.line, token.position.end.column, token.position.end.char);
}
index++;
return { value: array, index: index };
}
else {
var value = parseValue(source, tokenList, index, settings);
index = value.index;
array.items.push(value.value);
state = arrayStates.VALUE;
}
break;
}
case arrayStates.VALUE: {
if (token.type === types_js_1.JsonTokenTypes.RIGHT_BRACKET) {
if (settings.verbose) {
array.position = new position_js_1.JsonPosition(startToken.position.start.line, startToken.position.start.column, startToken.position.start.char, token.position.end.line, token.position.end.column, token.position.end.char);
}
index++;
return { value: array, index: index };
}
else if (token.type === types_js_1.JsonTokenTypes.COMMA) {
state = arrayStates.COMMA;
index++;
}
else {
(0, error_js_1.error)((0, parseErrorTypes_js_1.unexpectedToken)(source.substring(token.position.start.char, token.position.end.char), token.position.start.line, token.position.start.column), source, token.position.start.line, token.position.start.column);
}
break;
}
case arrayStates.COMMA: {
// Allow for trailing commas and too many commas
if (token.type === types_js_1.JsonTokenTypes.COMMA || token.type === types_js_1.JsonTokenTypes.RIGHT_BRACKET) {
state = arrayStates.VALUE;
continue;
}
var value = parseValue(source, tokenList, index, settings);
index = value.index;
array.items.push(value.value);
state = arrayStates.VALUE;
break;
}
}
}
(0, error_js_1.error)((0, parseErrorTypes_js_1.unexpectedEnd)());
}
function parseDocument(source, tokenList, index, settings) {
// value | COMMENT*
var token = tokenList[index];
var tokenType = token.type;
var doc = ast_js_1.NodeFactory.fromType(ast_js_1.JsonNodeTypes.DOCUMENT);
while (tokenType === types_js_1.JsonTokenTypes.COMMENT) {
var comment = ast_js_1.NodeFactory.fromType(ast_js_1.JsonNodeTypes.COMMENT, token.value);
if (settings.verbose) {
comment.position = token.position;
}
doc.comments.push(comment);
index++;
token = tokenList[index];
tokenType = token.type;
}
doc.child = parseValue(source, tokenList, index, settings);
if (doc.child.index !== tokenList.length) {
index = doc.child.index;
while (index < tokenList.length && tokenList[index].type === types_js_1.JsonTokenTypes.COMMENT) {
token = tokenList[index];
tokenType = token.type;
doc.child.index = index;
var comment = ast_js_1.NodeFactory.fromType(ast_js_1.JsonNodeTypes.COMMENT, token.value);
if (settings.verbose) {
comment.position = token.position;
}
doc.comments.push(comment);
index++;
}
doc.child.index = index;
}
var final_index = doc.child.index;
if (!(doc.child instanceof ast_js_1.JsonNode) && doc.child.value) {
doc.child = doc.child.value;
}
return { value: doc, index: final_index };
}
function parse(source, settings) {
settings = Object.assign({}, defaultSettings, settings);
var tokenList = (0, tokenize_js_1.tokenize)(source, settings);
if (tokenList.length === 0) {
(0, error_js_1.error)((0, parseErrorTypes_js_1.unexpectedEnd)());
}
if (settings.junker === true) {
tokenList = (0, junker_js_1.junker)(tokenList, settings);
}
var value = parseDocument(source, tokenList, 0, settings);
if (value.index === tokenList.length || settings.junker === true) {
return value.value;
}
else {
var token = tokenList[value.index];
(0, error_js_1.error)((0, parseErrorTypes_js_1.unexpectedToken)(source.substring(token.position.start.char, token.position.end.char), token.position.start.line, token.position.start.column), source, token.position.start.line, token.position.start.column);
}
}