yaml-language-server-parser
Version:
This is a maintained fork of YAML-AST-PARSER specifically for the YAML Language server.
208 lines (188 loc) • 5.15 kB
text/typescript
/**
* Created by kor on 06/05/15.
*/
import YAMLException = require('./exception');
export enum Kind {
SCALAR,
MAPPING,
MAP,
SEQ,
ANCHOR_REF,
INCLUDE_REF
}
export interface YAMLDocument {
startPosition: number
endPosition: number
errors: YAMLException[]
}
export interface YAMLNode extends YAMLDocument {
startPosition: number
endPosition: number
kind: Kind
anchorId?: string
valueObject?: any
parent: YAMLNode
errors: YAMLException[]
/**
* @deprecated Inspect kind and cast to the appropriate subtype instead.
*/
value?: any
/**
* @deprecated Inspect kind and cast to the appropriate subtype instead.
*/
key?: any
/**
* @deprecated Inspect kind and cast to the appropriate subtype instead.
*/
mappings?: any
}
export interface YAMLAnchorReference extends YAMLNode {
referencesAnchor: string
value: YAMLNode
}
export interface YAMLScalar extends YAMLNode {
value: string
doubleQuoted?: boolean
singleQuoted?: boolean
plainScalar?: boolean
rawValue: string
}
export interface YAMLMapping extends YAMLNode {
key: YAMLNode
value: YAMLNode
}
export interface YAMLSequence extends YAMLNode {
items: YAMLNode[]
}
export interface YamlMap extends YAMLNode {
mappings: YAMLMapping[]
}
export function newMapping(key: YAMLNode, value: YAMLNode): YAMLMapping {
var end = (value ? value.endPosition : key.endPosition + 1); //FIXME.workaround, end should be defied by position of ':'
//console.log('key: ' + key.value + ' ' + key.startPosition + '..' + key.endPosition + ' ' + value + ' end: ' + end);
var node = {
key: key,
value: value,
startPosition: key.startPosition,
endPosition: end,
kind: Kind.MAPPING,
parent: null,
errors: []
};
return node
}
export function newAnchorRef(key: string, start: number, end: number, value: YAMLNode): YAMLAnchorReference {
return {
errors: [],
referencesAnchor: key,
value: value,
startPosition: start,
endPosition: end,
kind: Kind.ANCHOR_REF,
parent: null
}
}
export function newScalar(v: string | boolean | number = ""): YAMLScalar {
const result: YAMLScalar = {
errors: [],
startPosition: -1,
endPosition: -1,
value: "" + v,
kind: Kind.SCALAR,
parent: null,
doubleQuoted: false,
rawValue: "" + v,
};
if (typeof v !== "string") {
result.valueObject = v;
}
return result
}
export function newItems(): YAMLSequence {
return {
errors: [],
startPosition: -1,
endPosition: -1,
items: [],
kind: Kind.SEQ,
parent: null
}
}
export function newSeq(): YAMLSequence {
return newItems();
}
export function newMap(mappings?: YAMLMapping[]): YamlMap {
return {
errors: [],
startPosition: -1,
endPosition: -1,
mappings: mappings ? mappings : [],
kind: Kind.MAP,
parent: null
}
}
/**
* Compare nodes as it described in yaml spec https://yaml.org/spec/1.2/spec.html#equality//
*
* @param a
* @param b
*
* @returns true when the two nodes are equal and false otherwise
*/
export function isNodesEqual(a: YAMLNode, b: YAMLNode): boolean {
if (!a || !b) {
return false;
}
if (a.kind !== b.kind) {
return false;
}
if (a.kind === Kind.SCALAR) {
return (<YAMLScalar>a).value === (<YAMLScalar>b).value;
}
if (a.kind === Kind.SEQ) {
const aSeq = <YAMLSequence>a;
const bSeq = <YAMLSequence>b;
if (aSeq.items.length !== bSeq.items.length) {
return false;
}
for (let i = 0; i < aSeq.items.length; i++) {
const elementA = aSeq.items[i];
const elementB = bSeq.items[i];
if (!isNodesEqual(elementA, elementB)) {
return false;
}
}
return true;
}
if (a.kind === Kind.MAP) {
const aMap = <YamlMap>a;
const bMap = <YamlMap>b;
if (aMap.mappings.length !== bMap.mappings.length) {
return false;
}
for (const mapA of aMap.mappings) {
const keyA = mapA.key;
const valA = mapA.value;
const mapB = bMap.mappings.find(mapB => isNodesEqual(keyA, mapB.key));
if (mapB) {
if (!isNodesEqual(valA, mapB.value)) {
return false;
}
} else {
return false;
}
}
return true;
}
if (Kind.MAPPING === a.kind) {
const aAsMapping = <YAMLMapping>a;
const bAsMapping = <YAMLMapping>b;
// Recursively determine if the keys are equal
const eq = isNodesEqual(aAsMapping.key, bAsMapping.key);
return eq;
}
return false;
}
export function isYAMLNode(obj: any): obj is YAMLNode {
return obj.startPosition !== undefined && obj.endPosition !== undefined && obj.kind !== undefined;
}