langium
Version:
A language engineering tool for the Language Server Protocol
220 lines • 7.61 kB
JavaScript
/******************************************************************************
* Copyright 2022 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { hasBooleanType } from '../types-util.js';
import { InterfaceType, UnionType, isArrayType } from './types.js';
export function isPlainInterface(type) {
return !isPlainUnion(type);
}
export function isPlainUnion(type) {
return 'type' in type;
}
export function isPlainReferenceType(propertyType) {
return 'referenceType' in propertyType;
}
export function isPlainArrayType(propertyType) {
return 'elementType' in propertyType;
}
export function isPlainPropertyUnion(propertyType) {
return 'types' in propertyType;
}
export function isPlainValueType(propertyType) {
return 'value' in propertyType;
}
export function isPlainPrimitiveType(propertyType) {
return 'primitive' in propertyType;
}
export function isPlainStringType(propertyType) {
return 'string' in propertyType;
}
export function plainToTypes(plain) {
const interfaceTypes = new Map();
const unionTypes = new Map();
for (const interfaceValue of plain.interfaces) {
const type = new InterfaceType(interfaceValue.name, interfaceValue.declared, interfaceValue.abstract, interfaceValue.comment);
interfaceTypes.set(interfaceValue.name, type);
}
for (const unionValue of plain.unions) {
const type = new UnionType(unionValue.name, {
declared: unionValue.declared,
dataType: unionValue.dataType,
comment: unionValue.comment,
});
unionTypes.set(unionValue.name, type);
}
for (const interfaceValue of plain.interfaces) {
const type = interfaceTypes.get(interfaceValue.name);
for (const superTypeName of interfaceValue.superTypes) {
const superType = interfaceTypes.get(superTypeName) || unionTypes.get(superTypeName);
if (superType) {
type.superTypes.add(superType);
}
}
for (const subTypeName of interfaceValue.subTypes) {
const subType = interfaceTypes.get(subTypeName) || unionTypes.get(subTypeName);
if (subType) {
type.subTypes.add(subType);
}
}
for (const property of interfaceValue.properties) {
const prop = plainToProperty(property, interfaceTypes, unionTypes);
type.properties.push(prop);
}
}
for (const unionValue of plain.unions) {
const type = unionTypes.get(unionValue.name);
type.type = plainToPropertyType(unionValue.type, type, interfaceTypes, unionTypes);
}
return {
interfaces: Array.from(interfaceTypes.values()),
unions: Array.from(unionTypes.values())
};
}
function plainToProperty(property, interfaces, unions) {
const prop = {
name: property.name,
optional: property.optional,
astNodes: property.astNodes,
type: plainToPropertyType(property.type, undefined, interfaces, unions),
comment: property.comment,
};
if (property.defaultValue !== undefined) {
prop.defaultValue = property.defaultValue;
}
else if (hasBooleanType(prop.type)) {
prop.defaultValue = false;
}
else if (isArrayType(prop.type)) {
prop.defaultValue = [];
}
return prop;
}
function plainToPropertyType(type, union, interfaces, unions) {
if (isPlainArrayType(type)) {
return {
elementType: plainToPropertyType(type.elementType, union, interfaces, unions)
};
}
else if (isPlainReferenceType(type)) {
return {
referenceType: plainToPropertyType(type.referenceType, undefined, interfaces, unions),
isMulti: type.isMulti,
isSingle: type.isSingle
};
}
else if (isPlainPropertyUnion(type)) {
return {
types: type.types.map(e => plainToPropertyType(e, union, interfaces, unions))
};
}
else if (isPlainStringType(type)) {
return {
string: type.string
};
}
else if (isPlainPrimitiveType(type)) {
return {
primitive: type.primitive,
regex: type.regex
};
}
else if (isPlainValueType(type)) {
const value = interfaces.get(type.value) || unions.get(type.value);
if (!value) {
return {
primitive: 'unknown'
};
}
if (union) {
union.subTypes.add(value);
}
return {
value
};
}
else {
throw new Error('Invalid property type');
}
}
export function mergePropertyTypes(first, second) {
const { union: flattenedFirstUnion, array: flattenedFirstArray } = flattenPlainType(first);
const { union: flattenedSecondUnion, array: flattenedSecondArray } = flattenPlainType(second);
const flattenedUnion = mergeTypeUnion(flattenedFirstUnion, flattenedSecondUnion);
const flattenedArray = mergeTypeUnion(flattenedFirstArray, flattenedSecondArray);
if (flattenedArray.length > 0) {
flattenedUnion.push({
elementType: flattenedArray.length === 1 ? flattenedArray[0] : {
types: flattenedArray
}
});
}
if (flattenedUnion.length === 1) {
return flattenedUnion[0];
}
else {
return {
types: flattenedUnion
};
}
}
function mergeTypeUnion(first, second) {
const result = [...first];
for (const type of second) {
if (!includesType(result, type)) {
result.push(type);
}
else if (isPlainReferenceType(type)) {
// Adjust the existing reference type to also include the multi/single flags of the new type
const existing = result.find((e) => isPlainReferenceType(e) && typeEquals(e.referenceType, type.referenceType));
if (existing) {
existing.isMulti || (existing.isMulti = type.isMulti);
existing.isSingle || (existing.isSingle = type.isSingle);
}
}
}
return result;
}
function includesType(list, value) {
return list.some(e => typeEquals(e, value));
}
function typeEquals(first, second) {
if (isPlainArrayType(first) && isPlainArrayType(second)) {
return typeEquals(first.elementType, second.elementType);
}
else if (isPlainReferenceType(first) && isPlainReferenceType(second)) {
return typeEquals(first.referenceType, second.referenceType);
}
else if (isPlainValueType(first) && isPlainValueType(second)) {
return first.value === second.value;
}
else if (isPlainPrimitiveType(first) && isPlainPrimitiveType(second)) {
return first.primitive === second.primitive;
}
else {
return false;
}
}
export function flattenPlainType(type) {
if (isPlainPropertyUnion(type)) {
const flattened = type.types.flatMap(e => flattenPlainType(e));
return {
union: flattened.map(e => e.union).flat(),
array: flattened.map(e => e.array).flat()
};
}
else if (isPlainArrayType(type)) {
return {
array: flattenPlainType(type.elementType).union,
union: []
};
}
else {
return {
array: [],
union: [type]
};
}
}
//# sourceMappingURL=plain-types.js.map