alm
Version:
The best IDE for TypeScript
928 lines (927 loc) • 40.9 kB
JavaScript
/**
* From : https://github.com/Microsoft/vscode-json-languageservice/blob/master/src/parser/jsonParser.ts
*
* Modifications:
* - removed `localize` dependency
* - redirected `jsonc-parser` dependency
*/
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
;
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var Json = require("./jsonc-parser");
var jsonLocation_1 = require("./jsonLocation");
/** BAS the function that was provided by vscode-nls */
var localize_1 = require("./localize");
var ASTNode = /** @class */ (function () {
function ASTNode(parent, type, name, start, end) {
this.type = type;
this.name = name;
this.start = start;
this.end = end;
this.parent = parent;
}
ASTNode.prototype.getNodeLocation = function () {
var path = this.parent ? this.parent.getNodeLocation() : new jsonLocation_1.JSONLocation([]);
if (this.name) {
path = path.append(this.name);
}
return path;
};
ASTNode.prototype.getChildNodes = function () {
return [];
};
ASTNode.prototype.getValue = function () {
// override in children
return;
};
ASTNode.prototype.contains = function (offset, includeRightBound) {
if (includeRightBound === void 0) { includeRightBound = false; }
return offset >= this.start && offset < this.end || includeRightBound && offset === this.end;
};
ASTNode.prototype.visit = function (visitor) {
return visitor(this);
};
ASTNode.prototype.getNodeFromOffset = function (offset) {
var findNode = function (node) {
if (offset >= node.start && offset < node.end) {
var children = node.getChildNodes();
for (var i = 0; i < children.length && children[i].start <= offset; i++) {
var item = findNode(children[i]);
if (item) {
return item;
}
}
return node;
}
return null;
};
return findNode(this);
};
ASTNode.prototype.getNodeFromOffsetEndInclusive = function (offset) {
var findNode = function (node) {
if (offset >= node.start && offset <= node.end) {
var children = node.getChildNodes();
for (var i = 0; i < children.length && children[i].start <= offset; i++) {
var item = findNode(children[i]);
if (item) {
return item;
}
}
return node;
}
return null;
};
return findNode(this);
};
ASTNode.prototype.validate = function (schema, validationResult, matchingSchemas, offset) {
var _this = this;
if (offset === void 0) { offset = -1; }
if (offset !== -1 && !this.contains(offset)) {
return;
}
if (Array.isArray(schema.type)) {
if (schema.type.indexOf(this.type) === -1) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: schema.errorMessage || localize_1.localize('typeArrayMismatchWarning', 'Incorrect type. Expected one of {0}', schema.type.join(', '))
});
}
}
else if (schema.type) {
if (this.type !== schema.type) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: schema.errorMessage || localize_1.localize('typeMismatchWarning', 'Incorrect type. Expected "{0}"', schema.type)
});
}
}
if (Array.isArray(schema.allOf)) {
schema.allOf.forEach(function (subSchema) {
_this.validate(subSchema, validationResult, matchingSchemas, offset);
});
}
if (schema.not) {
var subValidationResult = new ValidationResult();
var subMatchingSchemas = [];
this.validate(schema.not, subValidationResult, subMatchingSchemas, offset);
if (!subValidationResult.hasErrors()) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: localize_1.localize('notSchemaWarning', "Matches a schema that is not allowed.")
});
}
if (matchingSchemas) {
subMatchingSchemas.forEach(function (ms) {
ms.inverted = !ms.inverted;
matchingSchemas.push(ms);
});
}
}
var testAlternatives = function (alternatives, maxOneMatch) {
var matches = [];
// remember the best match that is used for error messages
var bestMatch = null;
alternatives.forEach(function (subSchema) {
var subValidationResult = new ValidationResult();
var subMatchingSchemas = [];
_this.validate(subSchema, subValidationResult, subMatchingSchemas);
if (!subValidationResult.hasErrors()) {
matches.push(subSchema);
}
if (!bestMatch) {
bestMatch = { schema: subSchema, validationResult: subValidationResult, matchingSchemas: subMatchingSchemas };
}
else {
if (!maxOneMatch && !subValidationResult.hasErrors() && !bestMatch.validationResult.hasErrors()) {
// no errors, both are equally good matches
bestMatch.matchingSchemas.push.apply(bestMatch.matchingSchemas, subMatchingSchemas);
bestMatch.validationResult.propertiesMatches += subValidationResult.propertiesMatches;
bestMatch.validationResult.propertiesValueMatches += subValidationResult.propertiesValueMatches;
}
else {
var compareResult = subValidationResult.compare(bestMatch.validationResult);
if (compareResult > 0) {
// our node is the best matching so far
bestMatch = { schema: subSchema, validationResult: subValidationResult, matchingSchemas: subMatchingSchemas };
}
else if (compareResult === 0) {
// there's already a best matching but we are as good
bestMatch.matchingSchemas.push.apply(bestMatch.matchingSchemas, subMatchingSchemas);
}
}
}
});
if (matches.length > 1 && maxOneMatch) {
validationResult.warnings.push({
location: { start: _this.start, end: _this.start + 1 },
message: localize_1.localize('oneOfWarning', "Matches multiple schemas when only one must validate.")
});
}
if (bestMatch !== null) {
validationResult.merge(bestMatch.validationResult);
validationResult.propertiesMatches += bestMatch.validationResult.propertiesMatches;
validationResult.propertiesValueMatches += bestMatch.validationResult.propertiesValueMatches;
if (matchingSchemas) {
matchingSchemas.push.apply(matchingSchemas, bestMatch.matchingSchemas);
}
}
return matches.length;
};
if (Array.isArray(schema.anyOf)) {
testAlternatives(schema.anyOf, false);
}
if (Array.isArray(schema.oneOf)) {
testAlternatives(schema.oneOf, true);
}
if (Array.isArray(schema.enum)) {
if (schema.enum.indexOf(this.getValue()) === -1) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: localize_1.localize('enumWarning', 'Value is not an accepted value. Valid values: {0}', JSON.stringify(schema.enum))
});
}
else {
validationResult.enumValueMatch = true;
}
}
if (matchingSchemas !== null) {
matchingSchemas.push({ node: this, schema: schema });
}
};
return ASTNode;
}());
exports.ASTNode = ASTNode;
var NullASTNode = /** @class */ (function (_super) {
__extends(NullASTNode, _super);
function NullASTNode(parent, name, start, end) {
return _super.call(this, parent, 'null', name, start, end) || this;
}
NullASTNode.prototype.getValue = function () {
return null;
};
return NullASTNode;
}(ASTNode));
exports.NullASTNode = NullASTNode;
var BooleanASTNode = /** @class */ (function (_super) {
__extends(BooleanASTNode, _super);
function BooleanASTNode(parent, name, value, start, end) {
var _this = _super.call(this, parent, 'boolean', name, start, end) || this;
_this.value = value;
return _this;
}
BooleanASTNode.prototype.getValue = function () {
return this.value;
};
return BooleanASTNode;
}(ASTNode));
exports.BooleanASTNode = BooleanASTNode;
var ArrayASTNode = /** @class */ (function (_super) {
__extends(ArrayASTNode, _super);
function ArrayASTNode(parent, name, start, end) {
var _this = _super.call(this, parent, 'array', name, start, end) || this;
_this.items = [];
return _this;
}
ArrayASTNode.prototype.getChildNodes = function () {
return this.items;
};
ArrayASTNode.prototype.getValue = function () {
return this.items.map(function (v) { return v.getValue(); });
};
ArrayASTNode.prototype.addItem = function (item) {
if (item) {
this.items.push(item);
return true;
}
return false;
};
ArrayASTNode.prototype.visit = function (visitor) {
var ctn = visitor(this);
for (var i = 0; i < this.items.length && ctn; i++) {
ctn = this.items[i].visit(visitor);
}
return ctn;
};
ArrayASTNode.prototype.validate = function (schema, validationResult, matchingSchemas, offset) {
var _this = this;
if (offset === void 0) { offset = -1; }
if (offset !== -1 && !this.contains(offset)) {
return;
}
_super.prototype.validate.call(this, schema, validationResult, matchingSchemas, offset);
if (Array.isArray(schema.items)) {
var subSchemas_1 = schema.items;
subSchemas_1.forEach(function (subSchema, index) {
var itemValidationResult = new ValidationResult();
var item = _this.items[index];
if (item) {
item.validate(subSchema, itemValidationResult, matchingSchemas, offset);
validationResult.mergePropertyMatch(itemValidationResult);
}
else if (_this.items.length >= subSchemas_1.length) {
validationResult.propertiesValueMatches++;
}
});
if (schema.additionalItems === false && this.items.length > subSchemas_1.length) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: localize_1.localize('additionalItemsWarning', 'Array has too many items according to schema. Expected {0} or fewer', subSchemas_1.length)
});
}
else if (this.items.length >= subSchemas_1.length) {
validationResult.propertiesValueMatches += (this.items.length - subSchemas_1.length);
}
}
else if (schema.items) {
this.items.forEach(function (item) {
var itemValidationResult = new ValidationResult();
item.validate(schema.items, itemValidationResult, matchingSchemas, offset);
validationResult.mergePropertyMatch(itemValidationResult);
});
}
if (schema.minItems && this.items.length < schema.minItems) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: localize_1.localize('minItemsWarning', 'Array has too few items. Expected {0} or more', schema.minItems)
});
}
if (schema.maxItems && this.items.length > schema.maxItems) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: localize_1.localize('maxItemsWarning', 'Array has too many items. Expected {0} or fewer', schema.minItems)
});
}
if (schema.uniqueItems === true) {
var values_1 = this.items.map(function (node) {
return node.getValue();
});
var duplicates = values_1.some(function (value, index) {
return index !== values_1.lastIndexOf(value);
});
if (duplicates) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: localize_1.localize('uniqueItemsWarning', 'Array has duplicate items')
});
}
}
};
return ArrayASTNode;
}(ASTNode));
exports.ArrayASTNode = ArrayASTNode;
var NumberASTNode = /** @class */ (function (_super) {
__extends(NumberASTNode, _super);
function NumberASTNode(parent, name, start, end) {
var _this = _super.call(this, parent, 'number', name, start, end) || this;
_this.isInteger = true;
_this.value = Number.NaN;
return _this;
}
NumberASTNode.prototype.getValue = function () {
return this.value;
};
NumberASTNode.prototype.validate = function (schema, validationResult, matchingSchemas, offset) {
if (offset === void 0) { offset = -1; }
if (offset !== -1 && !this.contains(offset)) {
return;
}
// work around type validation in the base class
var typeIsInteger = false;
if (schema.type === 'integer' || (Array.isArray(schema.type) && schema.type.indexOf('integer') !== -1)) {
typeIsInteger = true;
}
if (typeIsInteger && this.isInteger === true) {
this.type = 'integer';
}
_super.prototype.validate.call(this, schema, validationResult, matchingSchemas, offset);
this.type = 'number';
var val = this.getValue();
if (typeof schema.multipleOf === 'number') {
if (val % schema.multipleOf !== 0) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: localize_1.localize('multipleOfWarning', 'Value is not divisible by {0}', schema.multipleOf)
});
}
}
if (typeof schema.minimum === 'number') {
if (schema.exclusiveMinimum && val <= schema.minimum) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: localize_1.localize('exclusiveMinimumWarning', 'Value is below the exclusive minimum of {0}', schema.minimum)
});
}
if (!schema.exclusiveMinimum && val < schema.minimum) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: localize_1.localize('minimumWarning', 'Value is below the minimum of {0}', schema.minimum)
});
}
}
if (typeof schema.maximum === 'number') {
if (schema.exclusiveMaximum && val >= schema.maximum) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: localize_1.localize('exclusiveMaximumWarning', 'Value is above the exclusive maximum of {0}', schema.maximum)
});
}
if (!schema.exclusiveMaximum && val > schema.maximum) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: localize_1.localize('maximumWarning', 'Value is above the maximum of {0}', schema.maximum)
});
}
}
};
return NumberASTNode;
}(ASTNode));
exports.NumberASTNode = NumberASTNode;
var StringASTNode = /** @class */ (function (_super) {
__extends(StringASTNode, _super);
function StringASTNode(parent, name, isKey, start, end) {
var _this = _super.call(this, parent, 'string', name, start, end) || this;
_this.isKey = isKey;
_this.value = '';
return _this;
}
StringASTNode.prototype.getValue = function () {
return this.value;
};
StringASTNode.prototype.validate = function (schema, validationResult, matchingSchemas, offset) {
if (offset === void 0) { offset = -1; }
if (offset !== -1 && !this.contains(offset)) {
return;
}
_super.prototype.validate.call(this, schema, validationResult, matchingSchemas, offset);
if (schema.minLength && this.value.length < schema.minLength) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: localize_1.localize('minLengthWarning', 'String is shorter than the minimum length of ', schema.minLength)
});
}
if (schema.maxLength && this.value.length > schema.maxLength) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: localize_1.localize('maxLengthWarning', 'String is shorter than the maximum length of ', schema.maxLength)
});
}
if (schema.pattern) {
var regex = new RegExp(schema.pattern);
if (!regex.test(this.value)) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: schema.errorMessage || localize_1.localize('patternWarning', 'String does not match the pattern of "{0}"', schema.pattern)
});
}
}
};
return StringASTNode;
}(ASTNode));
exports.StringASTNode = StringASTNode;
var PropertyASTNode = /** @class */ (function (_super) {
__extends(PropertyASTNode, _super);
function PropertyASTNode(parent, key) {
var _this = _super.call(this, parent, 'property', null, key.start) || this;
_this.key = key;
key.parent = _this;
key.name = key.value;
_this.colonOffset = -1;
return _this;
}
PropertyASTNode.prototype.getChildNodes = function () {
return this.value ? [this.key, this.value] : [this.key];
};
PropertyASTNode.prototype.setValue = function (value) {
this.value = value;
return value !== null;
};
PropertyASTNode.prototype.visit = function (visitor) {
return visitor(this) && this.key.visit(visitor) && this.value && this.value.visit(visitor);
};
PropertyASTNode.prototype.validate = function (schema, validationResult, matchingSchemas, offset) {
if (offset === void 0) { offset = -1; }
if (offset !== -1 && !this.contains(offset)) {
return;
}
if (this.value) {
this.value.validate(schema, validationResult, matchingSchemas, offset);
}
};
return PropertyASTNode;
}(ASTNode));
exports.PropertyASTNode = PropertyASTNode;
var ObjectASTNode = /** @class */ (function (_super) {
__extends(ObjectASTNode, _super);
function ObjectASTNode(parent, name, start, end) {
var _this = _super.call(this, parent, 'object', name, start, end) || this;
_this.properties = [];
return _this;
}
ObjectASTNode.prototype.getChildNodes = function () {
return this.properties;
};
ObjectASTNode.prototype.addProperty = function (node) {
if (!node) {
return false;
}
this.properties.push(node);
return true;
};
ObjectASTNode.prototype.getFirstProperty = function (key) {
for (var i = 0; i < this.properties.length; i++) {
if (this.properties[i].key.value === key) {
return this.properties[i];
}
}
return null;
};
ObjectASTNode.prototype.getKeyList = function () {
return this.properties.map(function (p) { return p.key.getValue(); });
};
ObjectASTNode.prototype.getValue = function () {
var value = {};
this.properties.forEach(function (p) {
var v = p.value && p.value.getValue();
if (v) {
value[p.key.getValue()] = v;
}
});
return value;
};
ObjectASTNode.prototype.visit = function (visitor) {
var ctn = visitor(this);
for (var i = 0; i < this.properties.length && ctn; i++) {
ctn = this.properties[i].visit(visitor);
}
return ctn;
};
ObjectASTNode.prototype.validate = function (schema, validationResult, matchingSchemas, offset) {
var _this = this;
if (offset === void 0) { offset = -1; }
if (offset !== -1 && !this.contains(offset)) {
return;
}
_super.prototype.validate.call(this, schema, validationResult, matchingSchemas, offset);
var seenKeys = {};
var unprocessedProperties = [];
this.properties.forEach(function (node) {
var key = node.key.value;
seenKeys[key] = node.value;
unprocessedProperties.push(key);
});
if (Array.isArray(schema.required)) {
schema.required.forEach(function (propertyName) {
if (!seenKeys[propertyName]) {
var key = _this.parent && _this.parent && _this.parent.key;
var location_1 = key ? { start: key.start, end: key.end } : { start: _this.start, end: _this.start + 1 };
validationResult.warnings.push({
location: location_1,
message: localize_1.localize('MissingRequiredPropWarning', 'Missing property "{0}"', propertyName)
});
}
});
}
var propertyProcessed = function (prop) {
var index = unprocessedProperties.indexOf(prop);
while (index >= 0) {
unprocessedProperties.splice(index, 1);
index = unprocessedProperties.indexOf(prop);
}
};
if (schema.properties) {
Object.keys(schema.properties).forEach(function (propertyName) {
propertyProcessed(propertyName);
var prop = schema.properties[propertyName];
var child = seenKeys[propertyName];
if (child) {
var propertyvalidationResult = new ValidationResult();
child.validate(prop, propertyvalidationResult, matchingSchemas, offset);
validationResult.mergePropertyMatch(propertyvalidationResult);
}
});
}
if (schema.patternProperties) {
Object.keys(schema.patternProperties).forEach(function (propertyPattern) {
var regex = new RegExp(propertyPattern);
unprocessedProperties.slice(0).forEach(function (propertyName) {
if (regex.test(propertyName)) {
propertyProcessed(propertyName);
var child = seenKeys[propertyName];
if (child) {
var propertyvalidationResult = new ValidationResult();
child.validate(schema.patternProperties[propertyPattern], propertyvalidationResult, matchingSchemas, offset);
validationResult.mergePropertyMatch(propertyvalidationResult);
}
}
});
});
}
if (schema.additionalProperties) {
unprocessedProperties.forEach(function (propertyName) {
var child = seenKeys[propertyName];
if (child) {
var propertyvalidationResult = new ValidationResult();
child.validate(schema.additionalProperties, propertyvalidationResult, matchingSchemas, offset);
validationResult.mergePropertyMatch(propertyvalidationResult);
}
});
}
else if (schema.additionalProperties === false) {
if (unprocessedProperties.length > 0) {
unprocessedProperties.forEach(function (propertyName) {
var child = seenKeys[propertyName];
if (child) {
var propertyNode = child.parent;
validationResult.warnings.push({
location: { start: propertyNode.key.start, end: propertyNode.key.end },
message: localize_1.localize('DisallowedExtraPropWarning', 'Property {0} is not allowed', propertyName)
});
}
});
}
}
if (schema.maxProperties) {
if (this.properties.length > schema.maxProperties) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: localize_1.localize('MaxPropWarning', 'Object has more properties than limit of {0}', schema.maxProperties)
});
}
}
if (schema.minProperties) {
if (this.properties.length < schema.minProperties) {
validationResult.warnings.push({
location: { start: this.start, end: this.end },
message: localize_1.localize('MinPropWarning', 'Object has fewer properties than the required number of {0}', schema.minProperties)
});
}
}
if (schema.dependencies) {
Object.keys(schema.dependencies).forEach(function (key) {
var prop = seenKeys[key];
if (prop) {
if (Array.isArray(schema.dependencies[key])) {
var valueAsArray = schema.dependencies[key];
valueAsArray.forEach(function (requiredProp) {
if (!seenKeys[requiredProp]) {
validationResult.warnings.push({
location: { start: _this.start, end: _this.end },
message: localize_1.localize('RequiredDependentPropWarning', 'Object is missing property {0} required by property {1}', requiredProp, key)
});
}
else {
validationResult.propertiesValueMatches++;
}
});
}
else if (schema.dependencies[key]) {
var valueAsSchema = schema.dependencies[key];
var propertyvalidationResult = new ValidationResult();
_this.validate(valueAsSchema, propertyvalidationResult, matchingSchemas, offset);
validationResult.mergePropertyMatch(propertyvalidationResult);
}
}
});
}
};
return ObjectASTNode;
}(ASTNode));
exports.ObjectASTNode = ObjectASTNode;
var JSONDocumentConfig = /** @class */ (function () {
function JSONDocumentConfig() {
this.ignoreDanglingComma = false;
}
return JSONDocumentConfig;
}());
exports.JSONDocumentConfig = JSONDocumentConfig;
var ValidationResult = /** @class */ (function () {
function ValidationResult() {
this.errors = [];
this.warnings = [];
this.propertiesMatches = 0;
this.propertiesValueMatches = 0;
this.enumValueMatch = false;
}
ValidationResult.prototype.hasErrors = function () {
return !!this.errors.length || !!this.warnings.length;
};
ValidationResult.prototype.mergeAll = function (validationResults) {
var _this = this;
validationResults.forEach(function (validationResult) {
_this.merge(validationResult);
});
};
ValidationResult.prototype.merge = function (validationResult) {
this.errors = this.errors.concat(validationResult.errors);
this.warnings = this.warnings.concat(validationResult.warnings);
};
ValidationResult.prototype.mergePropertyMatch = function (propertyValidationResult) {
this.merge(propertyValidationResult);
this.propertiesMatches++;
if (propertyValidationResult.enumValueMatch || !propertyValidationResult.hasErrors() && propertyValidationResult.propertiesMatches) {
this.propertiesValueMatches++;
}
};
ValidationResult.prototype.compare = function (other) {
var hasErrors = this.hasErrors();
if (hasErrors !== other.hasErrors()) {
return hasErrors ? -1 : 1;
}
if (this.enumValueMatch !== other.enumValueMatch) {
return other.enumValueMatch ? -1 : 1;
}
if (this.propertiesValueMatches !== other.propertiesValueMatches) {
return this.propertiesValueMatches - other.propertiesValueMatches;
}
return this.propertiesMatches - other.propertiesMatches;
};
return ValidationResult;
}());
exports.ValidationResult = ValidationResult;
var JSONDocument = /** @class */ (function () {
function JSONDocument(config) {
this.config = config;
this.validationResult = new ValidationResult();
}
Object.defineProperty(JSONDocument.prototype, "errors", {
get: function () {
return this.validationResult.errors;
},
enumerable: true,
configurable: true
});
Object.defineProperty(JSONDocument.prototype, "warnings", {
get: function () {
return this.validationResult.warnings;
},
enumerable: true,
configurable: true
});
JSONDocument.prototype.getNodeFromOffset = function (offset) {
return this.root && this.root.getNodeFromOffset(offset);
};
JSONDocument.prototype.getNodeFromOffsetEndInclusive = function (offset) {
return this.root && this.root.getNodeFromOffsetEndInclusive(offset);
};
JSONDocument.prototype.visit = function (visitor) {
if (this.root) {
this.root.visit(visitor);
}
};
JSONDocument.prototype.validate = function (schema, matchingSchemas, offset) {
if (matchingSchemas === void 0) { matchingSchemas = null; }
if (offset === void 0) { offset = -1; }
if (this.root) {
this.root.validate(schema, this.validationResult, matchingSchemas, offset);
}
};
return JSONDocument;
}());
exports.JSONDocument = JSONDocument;
function parse(text, config) {
if (config === void 0) { config = new JSONDocumentConfig(); }
var _doc = new JSONDocument(config);
var _scanner = Json.createScanner(text, true);
function _accept(token) {
if (_scanner.getToken() === token) {
_scanner.scan();
return true;
}
return false;
}
function _error(message, node, skipUntilAfter, skipUntil) {
if (node === void 0) { node = null; }
if (skipUntilAfter === void 0) { skipUntilAfter = []; }
if (skipUntil === void 0) { skipUntil = []; }
if (_doc.errors.length === 0 || _doc.errors[0].location.start !== _scanner.getTokenOffset()) {
// ignore multiple errors on the same offset
var error = { message: message, location: { start: _scanner.getTokenOffset(), end: _scanner.getTokenOffset() + _scanner.getTokenLength() } };
_doc.errors.push(error);
}
if (node) {
_finalize(node, false);
}
if (skipUntilAfter.length + skipUntil.length > 0) {
var token = _scanner.getToken();
while (token !== Json.SyntaxKind.EOF) {
if (skipUntilAfter.indexOf(token) !== -1) {
_scanner.scan();
break;
}
else if (skipUntil.indexOf(token) !== -1) {
break;
}
token = _scanner.scan();
}
}
return node;
}
function _checkScanError() {
switch (_scanner.getTokenError()) {
case Json.ScanError.InvalidUnicode:
_error(localize_1.localize('InvalidUnicode', 'Invalid unicode sequence in string'));
return true;
case Json.ScanError.InvalidEscapeCharacter:
_error(localize_1.localize('InvalidEscapeCharacter', 'Invalid escape character in string'));
return true;
case Json.ScanError.UnexpectedEndOfNumber:
_error(localize_1.localize('UnexpectedEndOfNumber', 'Unexpected end of number'));
return true;
case Json.ScanError.UnexpectedEndOfComment:
_error(localize_1.localize('UnexpectedEndOfComment', 'Unexpected end of comment'));
return true;
case Json.ScanError.UnexpectedEndOfString:
_error(localize_1.localize('UnexpectedEndOfString', 'Unexpected end of string'));
return true;
}
return false;
}
function _finalize(node, scanNext) {
node.end = _scanner.getTokenOffset() + _scanner.getTokenLength();
if (scanNext) {
_scanner.scan();
}
return node;
}
function _parseArray(parent, name) {
if (_scanner.getToken() !== Json.SyntaxKind.OpenBracketToken) {
return null;
}
var node = new ArrayASTNode(parent, name, _scanner.getTokenOffset());
_scanner.scan(); // consume OpenBracketToken
var count = 0;
if (node.addItem(_parseValue(node, '' + count++))) {
while (_accept(Json.SyntaxKind.CommaToken)) {
if (!node.addItem(_parseValue(node, '' + count++)) && !_doc.config.ignoreDanglingComma) {
_error(localize_1.localize('ValueExpected', 'Value expected'));
}
}
}
if (_scanner.getToken() !== Json.SyntaxKind.CloseBracketToken) {
return _error(localize_1.localize('ExpectedCloseBracket', 'Expected comma or closing bracket'), node);
}
return _finalize(node, true);
}
function _parseProperty(parent, keysSeen) {
var key = _parseString(null, null, true);
if (!key) {
if (_scanner.getToken() === Json.SyntaxKind.Unknown) {
// give a more helpful error message
var value = _scanner.getTokenValue();
if (value.match(/^['\w]/)) {
_error(localize_1.localize('DoubleQuotesExpected', 'Property keys must be doublequoted'));
}
}
return null;
}
var node = new PropertyASTNode(parent, key);
if (keysSeen[key.value]) {
_doc.warnings.push({ location: { start: node.key.start, end: node.key.end }, message: localize_1.localize('DuplicateKeyWarning', "Duplicate object key") });
}
keysSeen[key.value] = true;
if (_scanner.getToken() === Json.SyntaxKind.ColonToken) {
node.colonOffset = _scanner.getTokenOffset();
}
else {
return _error(localize_1.localize('ColonExpected', 'Colon expected'), node, [], [Json.SyntaxKind.CloseBraceToken, Json.SyntaxKind.CommaToken]);
}
_scanner.scan(); // consume ColonToken
if (!node.setValue(_parseValue(node, key.value))) {
return _error(localize_1.localize('ValueExpected', 'Value expected'), node, [], [Json.SyntaxKind.CloseBraceToken, Json.SyntaxKind.CommaToken]);
}
node.end = node.value.end;
return node;
}
function _parseObject(parent, name) {
if (_scanner.getToken() !== Json.SyntaxKind.OpenBraceToken) {
return null;
}
var node = new ObjectASTNode(parent, name, _scanner.getTokenOffset());
_scanner.scan(); // consume OpenBraceToken
var keysSeen = {};
if (node.addProperty(_parseProperty(node, keysSeen))) {
while (_accept(Json.SyntaxKind.CommaToken)) {
if (!node.addProperty(_parseProperty(node, keysSeen)) && !_doc.config.ignoreDanglingComma) {
_error(localize_1.localize('PropertyExpected', 'Property expected'));
}
}
}
if (_scanner.getToken() !== Json.SyntaxKind.CloseBraceToken) {
return _error(localize_1.localize('ExpectedCloseBrace', 'Expected comma or closing brace'), node);
}
return _finalize(node, true);
}
function _parseString(parent, name, isKey) {
if (_scanner.getToken() !== Json.SyntaxKind.StringLiteral) {
return null;
}
var node = new StringASTNode(parent, name, isKey, _scanner.getTokenOffset());
node.value = _scanner.getTokenValue();
_checkScanError();
return _finalize(node, true);
}
function _parseNumber(parent, name) {
if (_scanner.getToken() !== Json.SyntaxKind.NumericLiteral) {
return null;
}
var node = new NumberASTNode(parent, name, _scanner.getTokenOffset());
if (!_checkScanError()) {
var tokenValue = _scanner.getTokenValue();
try {
var numberValue = JSON.parse(tokenValue);
if (typeof numberValue !== 'number') {
return _error(localize_1.localize('InvalidNumberFormat', 'Invalid number format'), node);
}
node.value = numberValue;
}
catch (e) {
return _error(localize_1.localize('InvalidNumberFormat', 'Invalid number format'), node);
}
node.isInteger = tokenValue.indexOf('.') === -1;
}
return _finalize(node, true);
}
function _parseLiteral(parent, name) {
var node;
switch (_scanner.getToken()) {
case Json.SyntaxKind.NullKeyword:
node = new NullASTNode(parent, name, _scanner.getTokenOffset());
break;
case Json.SyntaxKind.TrueKeyword:
node = new BooleanASTNode(parent, name, true, _scanner.getTokenOffset());
break;
case Json.SyntaxKind.FalseKeyword:
node = new BooleanASTNode(parent, name, false, _scanner.getTokenOffset());
break;
default:
return null;
}
return _finalize(node, true);
}
function _parseValue(parent, name) {
return _parseArray(parent, name) || _parseObject(parent, name) || _parseString(parent, name, false) || _parseNumber(parent, name) || _parseLiteral(parent, name);
}
_scanner.scan();
_doc.root = _parseValue(null, null);
if (!_doc.root) {
_error(localize_1.localize('Invalid symbol', 'Expected a JSON object, array or literal'));
}
else if (_scanner.getToken() !== Json.SyntaxKind.EOF) {
_error(localize_1.localize('End of file expected', 'End of file expected'));
}
return _doc;
}
exports.parse = parse;