WIP: Each type has an optional attached transformation

This commit is contained in:
Mark Probst 2018-04-14 08:04:59 -07:00
Родитель fb2375ba17
Коммит 48b63c5893
21 изменённых файлов: 317 добавлений и 295 удалений

Просмотреть файл

@ -115,7 +115,7 @@ export function findSimilarityCliques(
): ClassType[][] {
let unprocessedClasses = graph
.allNamedTypesSeparated()
.objects.filter(o => o instanceof ClassType && (includeFixedClasses || !o.isFixed))
.objects.filter(o => o instanceof ClassType && o.transformation === undefined && (includeFixedClasses || !o.isFixed))
.toArray() as ClassType[];
const cliques: ClassType[][] = [];

Просмотреть файл

@ -448,7 +448,7 @@ export abstract class ConvenienceRenderer extends Renderer {
.sortBy((_, n) => defined(names.get(defined(propertyNameds.get(n)))));
return sortedMap.toOrderedSet();
}
return t.children.toOrderedSet();
return t.getChildren();
};
protected get namedUnions(): OrderedSet<UnionType> {
@ -524,7 +524,6 @@ export abstract class ConvenienceRenderer extends Renderer {
},
_enumType => "enum",
_unionType => "union",
transformedType => typeNameForUnionMember(transformedType.targetType),
_dateType => "date",
_timeType => "time",
_dateTimeType => "date_time"
@ -898,7 +897,7 @@ export abstract class ConvenienceRenderer extends Renderer {
function visit(t: Type) {
if (visitedTypes.has(t)) return;
queue.push(...t.children.toArray());
queue.push(...t.getChildren().toArray());
visitedTypes = visitedTypes.add(t);
processed = processed.add(process(t));
}

Просмотреть файл

@ -54,7 +54,7 @@ export function cycleBreakerTypesForGraph(
if (visitedTypes.has(t)) return;
if (isImplicitCycleBreaker(t)) {
queue.push(...t.children.toArray());
queue.push(...t.getChildren().toArray());
} else {
const maybeBreaker = findBreaker(t, path, canBreakCycles);
if (maybeBreaker !== undefined) {
@ -63,7 +63,7 @@ export function cycleBreakerTypesForGraph(
}
const pathForChildren = path.unshift(t);
t.children.forEach(c => visit(c, pathForChildren));
t.getChildren().forEach(c => visit(c, pathForChildren));
}
visitedTypes = visitedTypes.add(t);

Просмотреть файл

@ -130,11 +130,11 @@ export function gatherNames(graph: TypeGraph, debugPrint: boolean): void {
members.forEach(memberType => {
addNames(memberType, names);
});
},
transformedType => {
addNames(transformedType.targetType, names);
}
);
if (t.transformation !== undefined) {
addNames(t.transformation.targetType, names);
}
}
}
@ -245,11 +245,11 @@ export function gatherNames(graph: TypeGraph, debugPrint: boolean): void {
const unionIsAncestor = unionHasGivenName || nullableFromUnion(unionType) === null;
const ancestorForMembers = unionIsAncestor ? unionType : ancestor;
members.forEach(memberType => processType(ancestorForMembers, memberType, undefined));
},
transformedType => {
processType(ancestor, transformedType.targetType, alternativeSuffix);
}
);
if (t.transformation !== undefined) {
processType(ancestor, t.transformation.targetType, alternativeSuffix);
}
}
}

Просмотреть файл

@ -2,7 +2,7 @@
import { Map, OrderedMap, OrderedSet, Set, Collection, isCollection } from "immutable";
import { PrimitiveTypeKind, Type, ClassProperty, Transformer } from "./Type";
import { PrimitiveTypeKind, Type, ClassProperty, Transformation } from "./Type";
import { combineTypeAttributesOfTypes } from "./TypeUtils";
import { TypeGraph } from "./TypeGraph";
import { TypeAttributes } from "./TypeAttributes";
@ -78,44 +78,57 @@ export class TypeReconstituter<TBuilder extends BaseGraphRewriteBuilder> {
return this._typeBuilder.reconstituteTypeRef(trefs);
}
getPrimitiveType(kind: PrimitiveTypeKind): void {
this.registerAndAddAttributes(this.builderForNewType().getPrimitiveType(kind, this._forwardingRef));
getPrimitiveType(kind: PrimitiveTypeKind, transformation: Transformation | undefined): void {
this.registerAndAddAttributes(
this.builderForNewType().getPrimitiveType(kind, transformation, this._forwardingRef)
);
}
getStringType(enumCases: OrderedMap<string, number> | undefined): void {
this.register(this.builderForNewType().getStringType(this._typeAttributes, enumCases, this._forwardingRef));
getStringType(enumCases: OrderedMap<string, number> | undefined, transformation: Transformation | undefined): void {
this.register(
this.builderForNewType().getStringType(this._typeAttributes, enumCases, transformation, this._forwardingRef)
);
}
getEnumType(cases: OrderedSet<string>): void {
this.register(this.builderForNewType().getEnumType(this._typeAttributes, cases, this._forwardingRef));
getEnumType(cases: OrderedSet<string>, transformation: Transformation | undefined): void {
this.register(
this.builderForNewType().getEnumType(this._typeAttributes, cases, transformation, this._forwardingRef)
);
}
getUniqueMapType(): void {
this.registerAndAddAttributes(this.builderForNewType().getUniqueMapType(this._forwardingRef));
}
getMapType(values: TypeRef): void {
this.registerAndAddAttributes(this.builderForNewType().getMapType(values, this._forwardingRef));
getMapType(values: TypeRef, transformation: Transformation | undefined): void {
this.registerAndAddAttributes(this.builderForNewType().getMapType(values, transformation, this._forwardingRef));
}
getUniqueArrayType(): void {
this.registerAndAddAttributes(this.builderForNewType().getUniqueArrayType(this._forwardingRef));
}
getArrayType(items: TypeRef): void {
this.registerAndAddAttributes(this.builderForNewType().getArrayType(items, this._forwardingRef));
getArrayType(items: TypeRef, transformation: Transformation | undefined): void {
this.registerAndAddAttributes(
this.builderForNewType().getArrayType(items, transformation, this._forwardingRef)
);
}
setArrayItems(items: TypeRef): void {
this.builderForSetting().setArrayItems(this.getResult(), items);
setArrayItems(items: TypeRef, transformation: Transformation | undefined): void {
this.builderForSetting().setArrayItems(this.getResult(), items, transformation);
}
getObjectType(properties: OrderedMap<string, ClassProperty>, additionalProperties: TypeRef | undefined): void {
getObjectType(
properties: OrderedMap<string, ClassProperty>,
additionalProperties: TypeRef | undefined,
transformation: Transformation | undefined
): void {
this.register(
this.builderForNewType().getUniqueObjectType(
this._typeAttributes,
properties,
additionalProperties,
transformation,
this._forwardingRef
)
);
@ -123,83 +136,98 @@ export class TypeReconstituter<TBuilder extends BaseGraphRewriteBuilder> {
getUniqueObjectType(
properties: OrderedMap<string, ClassProperty> | undefined,
additionalProperties: TypeRef | undefined
additionalProperties: TypeRef | undefined,
transformation: Transformation | undefined
): void {
this.register(
this.builderForNewType().getUniqueObjectType(
this._typeAttributes,
properties,
additionalProperties,
transformation,
this._forwardingRef
)
);
}
getClassType(properties: OrderedMap<string, ClassProperty>): void {
getClassType(properties: OrderedMap<string, ClassProperty>, transformation: Transformation | undefined): void {
if (this._makeClassUnique) {
this.getUniqueClassType(false, properties);
this.getUniqueClassType(false, properties, transformation);
return;
}
this.register(this.builderForNewType().getClassType(this._typeAttributes, properties, this._forwardingRef));
this.register(this.builderForNewType().getClassType(this._typeAttributes, properties, transformation, this._forwardingRef));
}
getUniqueClassType(isFixed: boolean, properties: OrderedMap<string, ClassProperty> | undefined): void {
getUniqueClassType(
isFixed: boolean,
properties: OrderedMap<string, ClassProperty> | undefined,
transformation: Transformation | undefined
): void {
this.register(
this.builderForNewType().getUniqueClassType(this._typeAttributes, isFixed, properties, this._forwardingRef)
this.builderForNewType().getUniqueClassType(
this._typeAttributes,
isFixed,
properties,
transformation,
this._forwardingRef
)
);
}
setObjectProperties(
properties: OrderedMap<string, ClassProperty>,
additionalProperties: TypeRef | undefined
additionalProperties: TypeRef | undefined,
transformation: Transformation | undefined
): void {
this.builderForSetting().setObjectProperties(this.getResult(), properties, additionalProperties);
this.builderForSetting().setObjectProperties(
this.getResult(),
properties,
additionalProperties,
transformation
);
}
getUnionType(members: OrderedSet<TypeRef>): void {
this.register(this.builderForNewType().getUnionType(this._typeAttributes, members, this._forwardingRef));
getUnionType(members: OrderedSet<TypeRef>, transformation: Transformation | undefined): void {
this.register(
this.builderForNewType().getUnionType(this._typeAttributes, members, transformation, this._forwardingRef)
);
}
getUniqueUnionType(): void {
this.register(
this.builderForNewType().getUniqueUnionType(this._typeAttributes, undefined, this._forwardingRef)
);
}
getIntersectionType(members: OrderedSet<TypeRef>): void {
this.register(this.builderForNewType().getIntersectionType(this._typeAttributes, members, this._forwardingRef));
}
getUniqueIntersectionType(members?: OrderedSet<TypeRef>): void {
this.register(
this.builderForNewType().getUniqueIntersectionType(this._typeAttributes, members, this._forwardingRef)
);
}
setSetOperationMembers(members: OrderedSet<TypeRef>): void {
this.builderForSetting().setSetOperationMembers(this.getResult(), members);
}
getTransformedType(transformer: Transformer, sourceRef: TypeRef, targetRef: TypeRef): void {
this.register(
this.builderForNewType().getTransformedType(
this.builderForNewType().getUniqueUnionType(
this._typeAttributes,
transformer,
sourceRef,
targetRef,
undefined,
undefined,
this._forwardingRef
)
);
}
getUniqueTransformedType(transformer: Transformer): void {
getIntersectionType(members: OrderedSet<TypeRef>, transformation: Transformation | undefined): void {
this.register(
this.builderForNewType().getUniqueTransformedType(this._typeAttributes, transformer, this._forwardingRef)
this.builderForNewType().getIntersectionType(
this._typeAttributes,
members,
transformation,
this._forwardingRef
)
);
}
setTransformedTypeTypes(sourceRef: TypeRef, targetRef: TypeRef): void {
this.builderForSetting().setTransformedTypeTypes(this.getResult(), sourceRef, targetRef);
getUniqueIntersectionType(): void {
this.register(
this.builderForNewType().getUniqueIntersectionType(
this._typeAttributes,
undefined,
undefined,
this._forwardingRef
)
);
}
setSetOperationMembers(members: OrderedSet<TypeRef>, transformation: Transformation | undefined): void {
this.builderForSetting().setSetOperationMembers(this.getResult(), members, transformation);
}
}

Просмотреть файл

@ -34,15 +34,15 @@ function replaceString(
const attributes = t.getAttributes();
const maybeEnumCases = shouldBeEnum(t);
if (maybeEnumCases !== undefined) {
return builder.getEnumType(attributes, maybeEnumCases.keySeq().toOrderedSet(), forwardingRef);
return builder.getEnumType(attributes, maybeEnumCases.keySeq().toOrderedSet(), undefined, forwardingRef);
}
return builder.getStringType(attributes, undefined, forwardingRef);
return builder.getStringType(attributes, undefined, undefined, forwardingRef);
}
// A union needs replacing if it contains more than one string type, one of them being
// a basic string type.
function unionNeedsReplacing(u: UnionType): OrderedSet<Type> | undefined {
const stringMembers = u.stringTypeMembers;
const stringMembers = u.stringTypeMembers.filter(t => t.transformation === undefined);
if (stringMembers.size <= 1) return undefined;
if (u.findMember("string") === undefined) return undefined;
return stringMembers;
@ -63,11 +63,12 @@ function replaceUnion(group: Set<UnionType>, builder: GraphRewriteBuilder<UnionT
return builder.getStringType(
combineTypeAttributes(stringAttributes, u.getAttributes()),
undefined,
undefined,
forwardingRef
);
}
types.push(builder.getStringType(stringAttributes, undefined));
return builder.getUnionType(u.getAttributes(), OrderedSet(types), forwardingRef);
return builder.getUnionType(u.getAttributes(), OrderedSet(types), undefined, forwardingRef);
}
export function inferEnums(
@ -77,7 +78,7 @@ export function inferEnums(
): TypeGraph {
const allStrings = graph
.allTypesUnordered()
.filter(t => t instanceof StringType)
.filter(t => t instanceof StringType && t.transformation === undefined)
.map(t => [t])
.toArray() as StringType[][];
return graph.rewrite("infer enums", stringTypeMapping, false, allStrings, debugPrintReconstitution, replaceString);

Просмотреть файл

@ -121,6 +121,7 @@ export function inferMaps(
unionBuilderForUnification(builder, false, false, false, conflateNumbers),
conflateNumbers
),
undefined,
forwardingRef
);
builder.addAttributes(tref, c.getAttributes());
@ -131,7 +132,7 @@ export function inferMaps(
ClassType
>;
const classesToReplace = allClasses
.filter(c => !c.isFixed && shouldBeMap(c.getProperties()) !== undefined)
.filter(c => c.transformation === undefined && !c.isFixed && shouldBeMap(c.getProperties()) !== undefined)
.toArray();
return graph.rewrite(
"infer maps",

Просмотреть файл

@ -49,7 +49,7 @@ class InferenceUnionBuilder extends UnionBuilder<TypeBuilder, NestedValueArray,
forwardingRef: TypeRef | undefined
): TypeRef {
const caseMap = OrderedMap(cases.map((c: string): [string, number] => [c, counts[c]]));
return this.typeBuilder.getStringType(typeAttributes, caseMap, forwardingRef);
return this.typeBuilder.getStringType(typeAttributes, caseMap, undefined, forwardingRef);
}
protected makeObject(
@ -179,9 +179,9 @@ export class TypeInference {
const propertyMap = OrderedMap(properties);
if (fixed) {
return this._typeBuilder.getUniqueClassType(typeAttributes, true, propertyMap, forwardingRef);
return this._typeBuilder.getUniqueClassType(typeAttributes, true, propertyMap, undefined, forwardingRef);
} else {
return this._typeBuilder.getClassType(typeAttributes, propertyMap, forwardingRef);
return this._typeBuilder.getClassType(typeAttributes, propertyMap, undefined, forwardingRef);
}
}
}

Просмотреть файл

@ -130,7 +130,6 @@ export class JSONSchemaRenderer extends ConvenienceRenderer {
return this.definitionForUnion(unionType);
}
},
transformedType => this.schemaForType(transformedType.sourceType),
_dateType => ({ type: "string", format: "date" }),
_timeType => ({ type: "string", format: "time" }),
_dateTimeType => ({ type: "string", format: "date-time" })

Просмотреть файл

@ -163,7 +163,7 @@ export class JavaScriptRenderer extends ConvenienceRenderer {
mapType => ["m(", this.typeMapTypeFor(mapType.values), ")"],
_enumType => panic("We handled this above"),
unionType => {
const children = unionType.children.map(this.typeMapTypeFor);
const children = unionType.getChildren().map(this.typeMapTypeFor);
return ["u(", ...intercalate(", ", children).toArray(), ")"];
}
);

Просмотреть файл

@ -487,7 +487,7 @@ export class RubyRenderer extends ConvenienceRenderer {
this.emitDescription(this.descriptionForType(u));
this.emitBlock(["class ", unionName, " < Dry::Struct"], () => {
const table: Sourcelike[][] = [];
this.forEachUnionMember(u, u.children, "none", null, (name, t) => {
this.forEachUnionMember(u, u.getChildren(), "none", null, (name, t) => {
table.push([["attribute :", name, ", "], [this.dryType(t, true)]]);
});
this.emitTable(table);
@ -499,8 +499,8 @@ export class RubyRenderer extends ConvenienceRenderer {
this.ensureBlankLine();
const [maybeNull, nonNulls] = removeNullFromUnion(u, false);
this.emitBlock("def self.from_dynamic!(d)", () => {
const memberNames = u.children.map((member: Type) => this.nameForUnionMember(u, member));
this.forEachUnionMember(u, u.children, "none", null, (name, t) => {
const memberNames = u.getChildren().map((member: Type) => this.nameForUnionMember(u, member));
this.forEachUnionMember(u, u.getChildren(), "none", null, (name, t) => {
const nilMembers = memberNames
.remove(name)
.toArray()

Просмотреть файл

@ -91,7 +91,7 @@ export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer {
_enumType => panic("We handled this above"),
unionType => {
if (this._inlineUnions || nullableFromUnion(unionType) !== null) {
const children = unionType.children.map(c => parenIfNeeded(this.sourceFor(c)));
const children = unionType.getChildren().map(c => parenIfNeeded(this.sourceFor(c)));
return multiWord(" | ", ...children.toArray());
} else {
return singleWord(this.nameForNamedType(unionType));
@ -123,7 +123,7 @@ export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer {
this.emitDescription(this.descriptionForType(u));
const children = multiWord(" | ", ...u.children.map(c => parenIfNeeded(this.sourceFor(c))).toArray());
const children = multiWord(" | ", ...u.getChildren().map(c => parenIfNeeded(this.sourceFor(c))).toArray());
this.emitLine("export type ", unionName, " = ", children.source, ";");
}

Просмотреть файл

@ -31,7 +31,7 @@ export function replaceObjectType(
}
function makeClass(): TypeRef {
return builder.getUniqueClassType(attributes, true, reconstituteProperties(), forwardingRef);
return builder.getUniqueClassType(attributes, true, reconstituteProperties(), undefined, forwardingRef);
}
function reconstituteAdditionalProperties(): TypeRef {
@ -43,7 +43,7 @@ export function replaceObjectType(
}
if (properties.isEmpty()) {
const tref = builder.getMapType(reconstituteAdditionalProperties(), forwardingRef);
const tref = builder.getMapType(reconstituteAdditionalProperties(), undefined, forwardingRef);
builder.addAttributes(tref, attributes);
return tref;
}
@ -77,12 +77,12 @@ export function replaceObjectType(
*/
}
const mapType = builder.getMapType(union, forwardingRef);
const mapType = builder.getMapType(union, undefined, forwardingRef);
builder.addAttributes(mapType, attributes);
return mapType;
}
const allObjectTypes = graph.allTypesUnordered().filter(t => t.kind === "object") as Set<ObjectType>;
const allObjectTypes = graph.allTypesUnordered().filter(t => t.kind === "object" && t.transformation === undefined) as Set<ObjectType>;
const objectTypesToReplace = leaveFullObjects
? allObjectTypes.filter(o => o.getProperties().isEmpty() || o.getAdditionalProperties() === undefined)
: allObjectTypes;

Просмотреть файл

@ -222,6 +222,7 @@ class IntersectionAccumulator
}
addType(t: Type): TypeAttributes {
assert(t.transformation === undefined, "We don't support intersections of transformations yet");
let attributes = t.getAttributes();
matchTypeExhaustive<void>(
t,
@ -247,9 +248,6 @@ class IntersectionAccumulator
);
this.addUnionSet(unionType.members);
},
_transformedType => {
return panic("We don't support intersections with transformed types yet");
},
dateType => this.addUnionSet(OrderedSet([dateType])),
timeType => this.addUnionSet(OrderedSet([timeType])),
dateTimeType => this.addUnionSet(OrderedSet([dateTimeType]))
@ -367,7 +365,7 @@ class IntersectionUnionBuilder extends UnionBuilder<
typeAttributes: TypeAttributes,
forwardingRef: TypeRef | undefined
): TypeRef {
return this.typeBuilder.getEnumType(typeAttributes, OrderedSet(cases), forwardingRef);
return this.typeBuilder.getEnumType(typeAttributes, OrderedSet(cases), undefined, forwardingRef);
}
protected makeObject(
@ -387,7 +385,7 @@ class IntersectionUnionBuilder extends UnionBuilder<
maybeAdditionalProperties === undefined
? undefined
: this.makeIntersection(maybeAdditionalProperties, emptyTypeAttributes);
return this.typeBuilder.getUniqueObjectType(typeAttributes, properties, additionalProperties, forwardingRef);
return this.typeBuilder.getUniqueObjectType(typeAttributes, properties, additionalProperties, undefined, forwardingRef);
}
protected makeArray(
@ -397,7 +395,7 @@ class IntersectionUnionBuilder extends UnionBuilder<
): TypeRef {
// FIXME: attributes
const itemsType = this.makeIntersection(arrays, Map());
const tref = this.typeBuilder.getArrayType(itemsType, forwardingRef);
const tref = this.typeBuilder.getArrayType(itemsType, undefined, forwardingRef);
this.typeBuilder.addAttributes(tref, typeAttributes);
return tref;
}
@ -414,7 +412,7 @@ export function resolveIntersections(
const intersections = types.filter(t => t instanceof IntersectionType) as Set<IntersectionType>;
const [members, intersectionAttributes] = setOperationMembersRecursively(intersections.toArray());
if (members.isEmpty()) {
const t = builder.getPrimitiveType("any", forwardingRef);
const t = builder.getPrimitiveType("any", undefined, forwardingRef);
builder.addAttributes(t, intersectionAttributes);
return t;
}

Просмотреть файл

@ -73,6 +73,14 @@ export function mapOptional<T, U>(f: (x: T) => U, x: T | undefined): U | undefin
return f(x);
}
export function ifUndefined<T>(x: any, f: () => T): T | undefined {
if (x === undefined) {
return f();
} else {
return undefined;
}
}
export function defined<T>(x: T | undefined): T {
if (x !== undefined) return x;
return panic("Defined value expected, but got undefined");

Просмотреть файл

@ -61,9 +61,13 @@ export class Transformation {
}
export abstract class Type {
constructor(readonly typeRef: TypeRef, readonly kind: TypeKind, private _transformation?: Transformation) {}
constructor(
readonly typeRef: TypeRef,
readonly kind: TypeKind,
private _transformation: Transformation | undefined
) {}
setTransformation(transformation: Transformation): void {
setTransformation(transformation: Transformation | undefined): void {
assert(this._transformation === undefined, "Tried to set transformation twice");
this._transformation = transformation;
}
@ -72,7 +76,7 @@ export abstract class Type {
return this._transformation;
}
get children(): OrderedSet<Type> {
getChildren(): OrderedSet<Type> {
if (this._transformation === undefined) {
return OrderedSet();
}
@ -82,7 +86,7 @@ export abstract class Type {
directlyReachableTypes<T>(setForType: (t: Type) => OrderedSet<T> | null): OrderedSet<T> {
const set = setForType(this);
if (set) return set;
return orderedSetUnion(this.children.map((t: Type) => t.directlyReachableTypes(setForType)));
return orderedSetUnion(this.getChildren().map((t: Type) => t.directlyReachableTypes(setForType)));
}
getAttributes(): TypeAttributes {
@ -103,15 +107,16 @@ export abstract class Type {
abstract get isNullable(): boolean;
abstract isPrimitive(): this is PrimitiveType;
abstract reconstitute<T extends BaseGraphRewriteBuilder>(builder: TypeReconstituter<T>): void;
reconstitute<T extends BaseGraphRewriteBuilder>(builder: TypeReconstituter<T>): void {
protected reconstituteTransformation<T extends BaseGraphRewriteBuilder>(
builder: TypeReconstituter<T>
): Transformation | undefined {
const tform = this._transformation;
if (tform === undefined) {
builder.setTransformationAndFinish();
return undefined;
}
builder.setTransformationAndFinish(
new Transformation(tform.transformer, builder.reconstitute(tform.targetType.typeRef))
);
return new Transformation(tform.transformer, builder.reconstitute(tform.targetType.typeRef));
}
get debugPrintKind(): string {
@ -226,15 +231,11 @@ export class PrimitiveType extends Type {
// @ts-ignore: This is initialized in the Type constructor
readonly kind: PrimitiveTypeKind;
constructor(typeRef: TypeRef, kind: PrimitiveTypeKind, checkKind: boolean = true) {
constructor(typeRef: TypeRef, kind: PrimitiveTypeKind, transformation: Transformation | undefined, checkKind: boolean = true) {
if (checkKind) {
assert(kind !== "string", "Cannot instantiate a PrimitiveType as string");
}
super(typeRef, kind);
}
get children(): OrderedSet<Type> {
return OrderedSet();
super(typeRef, kind, transformation);
}
get isNullable(): boolean {
@ -246,25 +247,21 @@ export class PrimitiveType extends Type {
}
reconstitute<T extends BaseGraphRewriteBuilder>(builder: TypeReconstituter<T>): void {
builder.getPrimitiveType(this.kind);
}
protected structuralEqualityStep(_other: Type, _queue: (a: Type, b: Type) => boolean): boolean {
return true;
builder.getPrimitiveType(this.kind, this.reconstituteTransformation(builder));
}
}
export class StringType extends PrimitiveType {
constructor(typeRef: TypeRef, readonly enumCases: OrderedMap<string, number> | undefined) {
super(typeRef, "string", false);
constructor(
typeRef: TypeRef,
readonly enumCases: OrderedMap<string, number> | undefined,
transformation: Transformation | undefined
) {
super(typeRef, "string", transformation, false);
}
reconstitute<T extends BaseGraphRewriteBuilder>(builder: TypeReconstituter<T>): void {
builder.getStringType(this.enumCases);
}
protected structuralEqualityStep(_other: Type, _queue: (a: Type, b: Type) => boolean): boolean {
return true;
builder.getStringType(this.enumCases, this.reconstituteTransformation(builder));
}
get debugPrintKind(): string {
@ -279,15 +276,16 @@ export class ArrayType extends Type {
// @ts-ignore: This is initialized in the Type constructor
readonly kind: "array";
constructor(typeRef: TypeRef, private _itemsRef?: TypeRef) {
super(typeRef, "array");
constructor(typeRef: TypeRef, private _itemsRef: TypeRef | undefined, transformation: Transformation | undefined) {
super(typeRef, "array", transformation);
}
setItems(itemsRef: TypeRef) {
setItems(itemsRef: TypeRef, transformation: Transformation | undefined) {
if (this._itemsRef !== undefined) {
return panic("Can only set array items once");
}
this._itemsRef = itemsRef;
super.setTransformation(transformation);
}
private getItemsRef(): TypeRef {
@ -301,8 +299,8 @@ export class ArrayType extends Type {
return this.getItemsRef().deref()[0];
}
get children(): OrderedSet<Type> {
return OrderedSet([this.items]);
getChildren(): OrderedSet<Type> {
return super.getChildren().add(this.items);
}
get isNullable(): boolean {
@ -318,14 +316,14 @@ export class ArrayType extends Type {
const maybeItems = builder.lookup(itemsRef);
if (maybeItems === undefined) {
builder.getUniqueArrayType();
builder.setArrayItems(builder.reconstitute(itemsRef));
builder.setArrayItems(builder.reconstitute(itemsRef), this.reconstituteTransformation(builder));
} else {
builder.getArrayType(maybeItems);
builder.getArrayType(maybeItems, this.reconstituteTransformation(builder));
}
}
protected structuralEqualityStep(other: ArrayType, queue: (a: Type, b: Type) => boolean): boolean {
return queue(this.items, other.items);
return super.structuralEqualityStep(other, queue) && queue(this.items, other.items);
}
}
@ -364,9 +362,10 @@ export class ObjectType extends Type {
kind: TypeKind,
readonly isFixed: boolean,
private _properties: OrderedMap<string, ClassProperty> | undefined,
private _additionalPropertiesRef: TypeRef | undefined
private _additionalPropertiesRef: TypeRef | undefined,
transformation: Transformation | undefined
) {
super(typeRef, kind);
super(typeRef, kind, transformation);
assert(kind === "object" || kind === "map" || kind === "class");
if (kind === "map") {
@ -381,7 +380,11 @@ export class ObjectType extends Type {
}
}
setProperties(properties: OrderedMap<string, ClassProperty>, additionalPropertiesRef: TypeRef | undefined) {
setProperties(
properties: OrderedMap<string, ClassProperty>,
additionalPropertiesRef: TypeRef | undefined,
transformation: Transformation | undefined
) {
if (this instanceof MapType) {
assert(properties.isEmpty(), "Cannot set properties on map type");
} else if (this._properties !== undefined) {
@ -394,6 +397,8 @@ export class ObjectType extends Type {
this._properties = properties;
this._additionalPropertiesRef = additionalPropertiesRef;
this.setTransformation(transformation);
}
getProperties(): OrderedMap<string, ClassProperty> {
@ -413,7 +418,7 @@ export class ObjectType extends Type {
return this._additionalPropertiesRef.deref()[0];
}
get children(): OrderedSet<Type> {
getChildren(): OrderedSet<Type> {
const children = this.getSortedProperties()
.map(p => p.type)
.toOrderedSet();
@ -421,7 +426,7 @@ export class ObjectType extends Type {
if (additionalProperties === undefined) {
return children;
}
return children.add(additionalProperties);
return super.getChildren().union(children.add(additionalProperties));
}
get isNullable(): boolean {
@ -443,20 +448,21 @@ export class ObjectType extends Type {
const properties = this.getProperties().map(
(cp, n) => new ClassProperty(defined(maybePropertyTypes.get(n)), cp.isOptional)
);
const tform = this.reconstituteTransformation(builder);
switch (this.kind) {
case "object":
assert(this.isFixed);
builder.getObjectType(properties, maybeAdditionalProperties);
builder.getObjectType(properties, maybeAdditionalProperties, tform);
break;
case "map":
builder.getMapType(defined(maybeAdditionalProperties));
builder.getMapType(defined(maybeAdditionalProperties), tform);
break;
case "class":
if (this.isFixed) {
builder.getUniqueClassType(true, properties);
builder.getUniqueClassType(true, properties, tform);
} else {
builder.getClassType(properties);
builder.getClassType(properties, tform);
}
break;
default:
@ -466,13 +472,13 @@ export class ObjectType extends Type {
switch (this.kind) {
case "object":
assert(this.isFixed);
builder.getUniqueObjectType(undefined, undefined);
builder.getUniqueObjectType(undefined, undefined, undefined);
break;
case "map":
builder.getUniqueMapType();
break;
case "class":
builder.getUniqueClassType(this.isFixed, undefined);
builder.getUniqueClassType(this.isFixed, undefined, undefined);
break;
default:
return panic(`Invalid object type kind ${this.kind}`);
@ -482,11 +488,13 @@ export class ObjectType extends Type {
cp => new ClassProperty(builder.reconstitute(cp.typeRef), cp.isOptional)
);
const additionalProperties = mapOptional(r => builder.reconstitute(r), this._additionalPropertiesRef);
builder.setObjectProperties(properties, additionalProperties);
builder.setObjectProperties(properties, additionalProperties, this.reconstituteTransformation(builder));
}
}
protected structuralEqualityStep(other: ObjectType, queue: (a: Type, b: Type) => boolean): boolean {
if (!super.structuralEqualityStep(other, queue)) return false;
const pa = this.getProperties();
const pb = other.getProperties();
if (pa.size !== pb.size) return false;
@ -512,8 +520,13 @@ export class ClassType extends ObjectType {
// @ts-ignore: This is initialized in the Type constructor
kind: "class";
constructor(typeRef: TypeRef, isFixed: boolean, properties: OrderedMap<string, ClassProperty> | undefined) {
super(typeRef, "class", isFixed, properties, undefined);
constructor(
typeRef: TypeRef,
isFixed: boolean,
properties: OrderedMap<string, ClassProperty> | undefined,
transformation: Transformation | undefined
) {
super(typeRef, "class", isFixed, properties, undefined, transformation);
}
}
@ -521,8 +534,8 @@ export class MapType extends ObjectType {
// @ts-ignore: This is initialized in the Type constructor
readonly kind: "map";
constructor(typeRef: TypeRef, valuesRef: TypeRef | undefined) {
super(typeRef, "map", false, OrderedMap(), valuesRef);
constructor(typeRef: TypeRef, valuesRef: TypeRef | undefined, transformation: Transformation | undefined) {
super(typeRef, "map", false, OrderedMap(), valuesRef, transformation);
}
// FIXME: Remove and use `getAdditionalProperties()` instead.
@ -535,12 +548,8 @@ export class EnumType extends Type {
// @ts-ignore: This is initialized in the Type constructor
kind: "enum";
constructor(typeRef: TypeRef, readonly cases: OrderedSet<string>) {
super(typeRef, "enum");
}
get children(): OrderedSet<Type> {
return OrderedSet();
constructor(typeRef: TypeRef, readonly cases: OrderedSet<string>, transformation: Transformation | undefined) {
super(typeRef, "enum", transformation);
}
get isNullable(): boolean {
@ -552,11 +561,11 @@ export class EnumType extends Type {
}
reconstitute<T extends BaseGraphRewriteBuilder>(builder: TypeReconstituter<T>): void {
builder.getEnumType(this.cases);
builder.getEnumType(this.cases, this.reconstituteTransformation(builder));
}
protected structuralEqualityStep(other: EnumType, _queue: (a: Type, b: Type) => void): boolean {
return this.cases.toSet().equals(other.cases.toSet());
protected structuralEqualityStep(other: EnumType, queue: (a: Type, b: Type) => boolean): boolean {
return super.structuralEqualityStep(other, queue) && this.cases.toSet().equals(other.cases.toSet());
}
}
@ -578,15 +587,24 @@ export function setOperationCasesEqual(
}
export abstract class SetOperationType extends Type {
constructor(typeRef: TypeRef, kind: TypeKind, private _memberRefs?: OrderedSet<TypeRef>) {
super(typeRef, kind);
constructor(
typeRef: TypeRef,
kind: TypeKind,
private _memberRefs: OrderedSet<TypeRef> | undefined,
transformation: Transformation | undefined
) {
super(typeRef, kind, transformation);
assert(transformation === undefined, "We don't support set operations with transformations yet");
}
setMembers(memberRefs: OrderedSet<TypeRef>): void {
setMembers(memberRefs: OrderedSet<TypeRef>, transformation: Transformation | undefined): void {
if (this._memberRefs !== undefined) {
return panic("Can only set map members once");
}
this._memberRefs = memberRefs;
assert(transformation === undefined, "We don't support set operations with transformations yet");
this.setTransformation(transformation);
}
protected getMemberRefs(): OrderedSet<TypeRef> {
@ -605,8 +623,8 @@ export abstract class SetOperationType extends Type {
return this.members.sortBy(t => t.kind);
}
get children(): OrderedSet<Type> {
return this.sortedMembers;
getChildren(): OrderedSet<Type> {
return super.getChildren().union(this.sortedMembers);
}
isPrimitive(): this is PrimitiveType {
@ -614,7 +632,7 @@ export abstract class SetOperationType extends Type {
}
protected structuralEqualityStep(other: SetOperationType, queue: (a: Type, b: Type) => boolean): boolean {
return setOperationCasesEqual(this.members, other.members, queue);
return super.structuralEqualityStep(other, queue) && setOperationCasesEqual(this.members, other.members, queue);
}
}
@ -622,8 +640,8 @@ export class IntersectionType extends SetOperationType {
// @ts-ignore: This is initialized in the Type constructor
kind: "intersection";
constructor(typeRef: TypeRef, memberRefs?: OrderedSet<TypeRef>) {
super(typeRef, "intersection", memberRefs);
constructor(typeRef: TypeRef, memberRefs: OrderedSet<TypeRef> | undefined, transformation: Transformation | undefined) {
super(typeRef, "intersection", memberRefs, transformation);
}
get isNullable(): boolean {
@ -635,9 +653,9 @@ export class IntersectionType extends SetOperationType {
const maybeMembers = builder.lookup(memberRefs);
if (maybeMembers === undefined) {
builder.getUniqueIntersectionType();
builder.setSetOperationMembers(builder.reconstitute(memberRefs));
builder.setSetOperationMembers(builder.reconstitute(memberRefs), this.reconstituteTransformation(builder));
} else {
builder.getIntersectionType(maybeMembers);
builder.getIntersectionType(maybeMembers, this.reconstituteTransformation(builder));
}
}
}
@ -646,16 +664,16 @@ export class UnionType extends SetOperationType {
// @ts-ignore: This is initialized in the Type constructor
kind: "union";
constructor(typeRef: TypeRef, memberRefs?: OrderedSet<TypeRef>) {
super(typeRef, "union", memberRefs);
constructor(typeRef: TypeRef, memberRefs: OrderedSet<TypeRef> | undefined, transformation: Transformation | undefined) {
super(typeRef, "union", memberRefs, transformation);
if (memberRefs !== undefined) {
messageAssert(!memberRefs.isEmpty(), ErrorMessage.IRNoEmptyUnions);
}
}
setMembers(memberRefs: OrderedSet<TypeRef>): void {
setMembers(memberRefs: OrderedSet<TypeRef>, transformation: Transformation | undefined): void {
messageAssert(!memberRefs.isEmpty(), ErrorMessage.IRNoEmptyUnions);
super.setMembers(memberRefs);
super.setMembers(memberRefs, transformation);
}
get stringTypeMembers(): OrderedSet<Type> {
@ -693,9 +711,9 @@ export class UnionType extends SetOperationType {
const maybeMembers = builder.lookup(memberRefs);
if (maybeMembers === undefined) {
builder.getUniqueUnionType();
builder.setSetOperationMembers(builder.reconstitute(memberRefs));
builder.setSetOperationMembers(builder.reconstitute(memberRefs), this.reconstituteTransformation(builder));
} else {
builder.getUnionType(maybeMembers);
builder.getUnionType(maybeMembers, this.reconstituteTransformation(builder));
}
}
}

Просмотреть файл

@ -1,6 +1,6 @@
"use strict";
import { Map, OrderedMap, OrderedSet, Set, List } from "immutable";
import { Map, OrderedMap, OrderedSet, Set } from "immutable";
import {
PrimitiveTypeKind,
@ -16,13 +16,12 @@ import {
ClassProperty,
IntersectionType,
ObjectType,
Transformer,
Transformation
} from "./Type";
import { removeNullFromUnion } from "./TypeUtils";
import { TypeGraph } from "./TypeGraph";
import { TypeAttributes, combineTypeAttributes, TypeAttributeKind } from "./TypeAttributes";
import { defined, assert, panic, setUnion, mapOptional } from "./Support";
import { defined, assert, panic, setUnion, mapOptional, ifUndefined } from "./Support";
export class TypeRef {
constructor(readonly graph: TypeGraph, readonly index: number) {}
@ -179,7 +178,7 @@ export class TypeBuilder {
protected addForwardingIntersection(forwardingRef: TypeRef, tref: TypeRef): TypeRef {
this._addedForwardingIntersection = true;
return this.addType(forwardingRef, tr => new IntersectionType(tr, OrderedSet([tref])), undefined);
return this.addType(forwardingRef, tr => new IntersectionType(tr, OrderedSet([tref]), undefined), undefined);
}
protected forwardIfNecessary(forwardingRef: TypeRef | undefined, tref: undefined): undefined;
@ -205,20 +204,21 @@ export class TypeBuilder {
private _classTypes: Map<Map<string, ClassProperty>, TypeRef> = Map();
private _unionTypes: Map<Set<TypeRef>, TypeRef> = Map();
private _intersectionTypes: Map<Set<TypeRef>, TypeRef> = Map();
private _transformedTypes: Map<List<any>, TypeRef> = Map();
getPrimitiveType(kind: PrimitiveTypeKind, forwardingRef?: TypeRef): TypeRef {
getPrimitiveType(kind: PrimitiveTypeKind, transformation?: Transformation, forwardingRef?: TypeRef): TypeRef {
assert(kind !== "string", "Use getStringType to create strings");
if (kind === "date") kind = this._stringTypeMapping.date;
if (kind === "time") kind = this._stringTypeMapping.time;
if (kind === "date-time") kind = this._stringTypeMapping.dateTime;
if (kind === "string") {
return this.getStringType(undefined, undefined, forwardingRef);
return this.getStringType(undefined, undefined, transformation, forwardingRef);
}
let tref = this.forwardIfNecessary(forwardingRef, this._primitiveTypes.get(kind));
let tref = ifUndefined(transformation, () => this.forwardIfNecessary(forwardingRef, this._primitiveTypes.get(kind)));
if (tref === undefined) {
tref = this.addType(forwardingRef, tr => new PrimitiveType(tr, kind), undefined);
this._primitiveTypes = this._primitiveTypes.set(kind, tref);
tref = this.addType(forwardingRef, tr => new PrimitiveType(tr, kind, transformation), undefined);
if (transformation === undefined) {
this._primitiveTypes = this._primitiveTypes.set(kind, tref);
}
}
return tref;
}
@ -226,6 +226,7 @@ export class TypeBuilder {
getStringType(
attributes: TypeAttributes | undefined,
cases: OrderedMap<string, number> | undefined,
transformation?: Transformation,
forwardingRef?: TypeRef
): TypeRef {
if (cases === undefined) {
@ -240,27 +241,32 @@ export class TypeBuilder {
// out whether we do want string types to have names (we most
// likely don't), and if not, still don't keep track of them.
let result: TypeRef;
if (this._noEnumStringType === undefined) {
result = this._noEnumStringType = this.addType(
if (this._noEnumStringType === undefined || transformation !== undefined) {
result = this.addType(
forwardingRef,
tr => new StringType(tr, undefined),
tr => new StringType(tr, undefined, transformation),
undefined
);
if (transformation === undefined) {
this._noEnumStringType = result;
}
} else {
result = this.forwardIfNecessary(forwardingRef, this._noEnumStringType);
}
this.addAttributes(this._noEnumStringType, attributes);
this.addAttributes(result, attributes);
return result;
}
return this.addType(forwardingRef, tr => new StringType(tr, cases), attributes);
return this.addType(forwardingRef, tr => new StringType(tr, cases, transformation), attributes);
}
getEnumType(attributes: TypeAttributes, cases: OrderedSet<string>, forwardingRef?: TypeRef): TypeRef {
getEnumType(attributes: TypeAttributes, cases: OrderedSet<string>, transformation?: Transformation, forwardingRef?: TypeRef): TypeRef {
const unorderedCases = cases.toSet();
let tref = this.forwardIfNecessary(forwardingRef, this._enumTypes.get(unorderedCases));
let tref = ifUndefined(transformation, () => this.forwardIfNecessary(forwardingRef, this._enumTypes.get(unorderedCases)));
if (tref === undefined) {
tref = this.addType(forwardingRef, tr => new EnumType(tr, cases), attributes);
this._enumTypes = this._enumTypes.set(unorderedCases, tref);
tref = this.addType(forwardingRef, tr => new EnumType(tr, cases, transformation), attributes);
if (transformation === undefined) {
this._enumTypes = this._enumTypes.set(unorderedCases, tref);
}
} else {
this.addAttributes(tref, attributes);
}
@ -271,18 +277,19 @@ export class TypeBuilder {
attributes: TypeAttributes,
properties: OrderedMap<string, ClassProperty> | undefined,
additionalProperties: TypeRef | undefined,
transformation?: Transformation,
forwardingRef?: TypeRef
): TypeRef {
properties = mapOptional(p => this.modifyPropertiesIfNecessary(p), properties);
return this.addType(
forwardingRef,
tref => new ObjectType(tref, "object", true, properties, additionalProperties),
tref => new ObjectType(tref, "object", true, properties, additionalProperties, transformation),
attributes
);
}
getUniqueMapType(forwardingRef?: TypeRef): TypeRef {
return this.addType(forwardingRef, tr => new MapType(tr, undefined), undefined);
return this.addType(forwardingRef, tr => new MapType(tr, undefined, undefined), undefined);
}
private registerMapType(values: TypeRef, tref: TypeRef): void {
@ -290,11 +297,13 @@ export class TypeBuilder {
this._mapTypes = this._mapTypes.set(values, tref);
}
getMapType(values: TypeRef, forwardingRef?: TypeRef): TypeRef {
let tref = this.forwardIfNecessary(forwardingRef, this._mapTypes.get(values));
getMapType(values: TypeRef, transformation?: Transformation, forwardingRef?: TypeRef): TypeRef {
let tref = ifUndefined(transformation, () => this.forwardIfNecessary(forwardingRef, this._mapTypes.get(values)));
if (tref === undefined) {
tref = this.addType(forwardingRef, tr => new MapType(tr, values), undefined);
this.registerMapType(values, tref);
tref = this.addType(forwardingRef, tr => new MapType(tr, values, transformation), undefined);
if (transformation === undefined) {
this.registerMapType(values, tref);
}
}
return tref;
}
@ -307,13 +316,16 @@ export class TypeBuilder {
setObjectProperties(
ref: TypeRef,
properties: OrderedMap<string, ClassProperty>,
additionalProperties: TypeRef | undefined
additionalProperties: TypeRef | undefined,
transformation?: Transformation
): void {
const type = ref.deref()[0];
if (!(type instanceof ObjectType)) {
return panic("Tried to set properties of non-object type");
}
type.setProperties(this.modifyPropertiesIfNecessary(properties), additionalProperties);
type.setProperties(this.modifyPropertiesIfNecessary(properties), additionalProperties, transformation);
if (transformation !== undefined) return;
if (type instanceof MapType) {
this.registerMapType(defined(additionalProperties), ref);
} else if (type instanceof ClassType) {
@ -324,7 +336,7 @@ export class TypeBuilder {
}
getUniqueArrayType(forwardingRef?: TypeRef): TypeRef {
return this.addType(forwardingRef, tr => new ArrayType(tr, undefined), undefined);
return this.addType(forwardingRef, tr => new ArrayType(tr, undefined, undefined), undefined);
}
private registerArrayType(items: TypeRef, tref: TypeRef): void {
@ -332,22 +344,26 @@ export class TypeBuilder {
this._arrayTypes = this._arrayTypes.set(items, tref);
}
getArrayType(items: TypeRef, forwardingRef?: TypeRef): TypeRef {
let tref = this.forwardIfNecessary(forwardingRef, this._arrayTypes.get(items));
getArrayType(items: TypeRef, transformation?: Transformation, forwardingRef?: TypeRef): TypeRef {
let tref = ifUndefined(transformation, () => this.forwardIfNecessary(forwardingRef, this._arrayTypes.get(items)));
if (tref === undefined) {
tref = this.addType(forwardingRef, tr => new ArrayType(tr, items), undefined);
this.registerArrayType(items, tref);
tref = this.addType(forwardingRef, tr => new ArrayType(tr, items, transformation), undefined);
if (transformation === undefined) {
this.registerArrayType(items, tref);
}
}
return tref;
}
setArrayItems(ref: TypeRef, items: TypeRef): void {
setArrayItems(ref: TypeRef, items: TypeRef, transformation?: Transformation): void {
const type = ref.deref()[0];
if (!(type instanceof ArrayType)) {
return panic("Tried to set items of non-array type");
}
type.setItems(items);
this.registerArrayType(items, ref);
type.setItems(items, transformation);
if (transformation === undefined) {
this.registerArrayType(items, ref);
}
}
modifyPropertiesIfNecessary(properties: OrderedMap<string, ClassProperty>): OrderedMap<string, ClassProperty> {
@ -363,13 +379,16 @@ export class TypeBuilder {
getClassType(
attributes: TypeAttributes,
properties: OrderedMap<string, ClassProperty>,
transformation?: Transformation,
forwardingRef?: TypeRef
): TypeRef {
properties = this.modifyPropertiesIfNecessary(properties);
let tref = this.forwardIfNecessary(forwardingRef, this._classTypes.get(properties.toMap()));
let tref = ifUndefined(transformation, () => this.forwardIfNecessary(forwardingRef, this._classTypes.get(properties.toMap())));
if (tref === undefined) {
tref = this.addType(forwardingRef, tr => new ClassType(tr, false, properties), attributes);
this.registerClassType(properties, tref);
tref = this.addType(forwardingRef, tr => new ClassType(tr, false, properties, transformation), attributes);
if (transformation === undefined) {
this.registerClassType(properties, tref);
}
} else {
this.addAttributes(tref, attributes);
}
@ -387,18 +406,21 @@ export class TypeBuilder {
attributes: TypeAttributes,
isFixed: boolean,
properties: OrderedMap<string, ClassProperty> | undefined,
transformation?: Transformation,
forwardingRef?: TypeRef
): TypeRef {
properties = mapOptional(p => this.modifyPropertiesIfNecessary(p), properties);
return this.addType(forwardingRef, tref => new ClassType(tref, isFixed, properties), attributes);
return this.addType(forwardingRef, tref => new ClassType(tref, isFixed, properties, transformation), attributes);
}
getUnionType(attributes: TypeAttributes, members: OrderedSet<TypeRef>, forwardingRef?: TypeRef): TypeRef {
getUnionType(attributes: TypeAttributes, members: OrderedSet<TypeRef>, transformation?: Transformation, forwardingRef?: TypeRef): TypeRef {
const unorderedMembers = members.toSet();
let tref = this.forwardIfNecessary(forwardingRef, this._unionTypes.get(unorderedMembers));
let tref = ifUndefined(transformation, () => this.forwardIfNecessary(forwardingRef, this._unionTypes.get(unorderedMembers)));
if (tref === undefined) {
tref = this.addType(forwardingRef, tr => new UnionType(tr, members), attributes);
this.registerUnionType(unorderedMembers, tref);
tref = this.addType(forwardingRef, tr => new UnionType(tr, members, transformation), attributes);
if (transformation === undefined) {
this.registerUnionType(unorderedMembers, tref);
}
} else {
this.addAttributes(tref, attributes);
}
@ -408,9 +430,10 @@ export class TypeBuilder {
getUniqueUnionType(
attributes: TypeAttributes,
members: OrderedSet<TypeRef> | undefined,
transformation?: Transformation,
forwardingRef?: TypeRef
): TypeRef {
return this.addType(forwardingRef, tref => new UnionType(tref, members), attributes);
return this.addType(forwardingRef, tref => new UnionType(tref, members, transformation), attributes);
}
private registerIntersectionType(members: Set<TypeRef>, tref: TypeRef): void {
@ -418,12 +441,14 @@ export class TypeBuilder {
this._intersectionTypes = this._intersectionTypes.set(members, tref);
}
getIntersectionType(attributes: TypeAttributes, members: OrderedSet<TypeRef>, forwardingRef?: TypeRef): TypeRef {
getIntersectionType(attributes: TypeAttributes, members: OrderedSet<TypeRef>, transformation?: Transformation, forwardingRef?: TypeRef): TypeRef {
const unorderedMembers = members.toSet();
let tref = this.forwardIfNecessary(forwardingRef, this._intersectionTypes.get(unorderedMembers));
let tref = ifUndefined(transformation, () => this.forwardIfNecessary(forwardingRef, this._intersectionTypes.get(unorderedMembers)));
if (tref === undefined) {
tref = this.addType(forwardingRef, tr => new IntersectionType(tr, members), attributes);
this.registerIntersectionType(unorderedMembers, tref);
tref = this.addType(forwardingRef, tr => new IntersectionType(tr, members, transformation), attributes);
if (transformation === undefined) {
this.registerIntersectionType(unorderedMembers, tref);
}
} else {
this.addAttributes(tref, attributes);
}
@ -433,17 +458,18 @@ export class TypeBuilder {
getUniqueIntersectionType(
attributes: TypeAttributes,
members: OrderedSet<TypeRef> | undefined,
transformation?: Transformation,
forwardingRef?: TypeRef
): TypeRef {
return this.addType(forwardingRef, tref => new IntersectionType(tref, members), attributes);
return this.addType(forwardingRef, tref => new IntersectionType(tref, members, transformation), attributes);
}
setSetOperationMembers(ref: TypeRef, members: OrderedSet<TypeRef>): void {
setSetOperationMembers(ref: TypeRef, members: OrderedSet<TypeRef>, transformation?: Transformation): void {
const type = ref.deref()[0];
if (!(type instanceof UnionType || type instanceof IntersectionType)) {
return panic("Tried to set members of non-set-operation type");
}
type.setMembers(members);
type.setMembers(members, transformation);
const unorderedMembers = members.toSet();
if (type instanceof UnionType) {
this.registerUnionType(unorderedMembers, ref);
@ -454,59 +480,7 @@ export class TypeBuilder {
}
}
setTransformation(ref: TypeRef, transformation: Transformation): void {
const type = ref.deref()[0];
type.setTransformation(transformation);
}
setLostTypeAttributes(): void {
return;
}
private registerTransformedType(
transformer: Transformer,
sourceRef: TypeRef,
targetRef: TypeRef,
tref: TypeRef
): void {
const key = List([transformer, sourceRef, targetRef]);
if (this._transformedTypes.has(key)) return;
this._transformedTypes = this._transformedTypes.set(key, tref);
}
getTransformedType(
attributes: TypeAttributes,
transformer: Transformer,
sourceRef: TypeRef,
targetRef: TypeRef,
forwardingRef?: TypeRef
): TypeRef {
const key = List([transformer, sourceRef, targetRef]);
let tref = this.forwardIfNecessary(forwardingRef, this._transformedTypes.get(key));
if (tref === undefined) {
tref = this.addType(
forwardingRef,
tr => new TransformedType(tr, transformer, sourceRef, targetRef),
attributes
);
this.registerTransformedType(transformer, sourceRef, targetRef, tref);
} else {
this.addAttributes(tref, attributes);
}
return tref;
}
getUniqueTransformedType(attributes: TypeAttributes, transformer: Transformer, forwardingRef?: TypeRef): TypeRef {
return this.addType(forwardingRef, tref => new TransformedType(tref, transformer), attributes);
}
setTransformedTypeTypes(ref: TypeRef, sourceRef: TypeRef, targetRef: TypeRef): void {
const type = ref.deref()[0];
if (!(type instanceof TransformedType)) {
return panic("Tried to set types of non-transformed type");
}
type.setTypes(sourceRef, targetRef);
this.registerTransformedType(type.transformer, sourceRef, targetRef, ref);
}
}

Просмотреть файл

@ -184,7 +184,7 @@ export class TypeGraph {
types = types.push(t);
}
const children = childrenOfType !== undefined ? childrenOfType(t) : t.children;
const children = childrenOfType !== undefined ? childrenOfType(t) : t.getChildren();
children.forEach(addFromType);
if (!topDown && required) {
@ -345,7 +345,7 @@ export class TypeGraph {
if (this._parents === undefined) {
const parents = defined(this._types).map(_ => Set());
this.allTypesUnordered().forEach(p => {
p.children.forEach(c => {
p.getChildren().forEach(c => {
const index = c.typeRef.index;
parents[index] = parents[index].add(p);
});
@ -361,7 +361,7 @@ export class TypeGraph {
const t = types[i];
const parts: string[] = [];
parts.push(`${t.debugPrintKind}${t.hasNames ? ` ${t.getCombinedName()}` : ""}`);
const children = t.children;
const children = t.getChildren();
if (!children.isEmpty()) {
parts.push(`children ${children.map(c => c.typeRef.index).join(",")}`);
}
@ -385,6 +385,7 @@ export function noneToAny(
if (noneTypes.size === 0) {
return graph;
}
assert(noneTypes.every(t => t.transformation === undefined), "We don't support none types with transformations");
assert(noneTypes.size === 1, "Cannot have more than one none type");
return graph.rewrite(
"none to any",
@ -393,7 +394,7 @@ export function noneToAny(
[noneTypes.toArray()],
debugPrintReconstitution,
(types, builder, forwardingRef) => {
const tref = builder.getPrimitiveType("any", forwardingRef);
const tref = builder.getPrimitiveType("any", undefined, forwardingRef);
const attributes = combineTypeAttributesOfTypes(types);
builder.addAttributes(tref, attributes);
return tref;
@ -428,15 +429,15 @@ export function optionalToNullable(
return new ClassProperty(ref, false);
});
if (c.isFixed) {
return builder.getUniqueClassType(c.getAttributes(), true, properties, forwardingRef);
return builder.getUniqueClassType(c.getAttributes(), true, properties, undefined, forwardingRef);
} else {
return builder.getClassType(c.getAttributes(), properties, forwardingRef);
return builder.getClassType(c.getAttributes(), properties, undefined, forwardingRef);
}
}
const classesWithOptional = graph
.allTypesUnordered()
.filter(t => t instanceof ClassType && t.getProperties().some(p => p.isOptional));
.filter(t => t instanceof ClassType && t.transformation === undefined && t.getProperties().some(p => p.isOptional));
const replacementGroups = classesWithOptional.map(c => [c as ClassType]).toArray();
if (classesWithOptional.size === 0) {
return graph;

Просмотреть файл

@ -15,8 +15,7 @@ import {
ClassType,
ClassProperty,
SetOperationType,
UnionType,
TransformedType
UnionType
} from "./Type";
export function assertIsObject(t: Type): ObjectType {
@ -213,7 +212,6 @@ export function matchTypeExhaustive<U>(
objectType: (objectType: ObjectType) => U,
enumType: (enumType: EnumType) => U,
unionType: (unionType: UnionType) => U,
transformedType: (transformedType: TransformedType) => U,
dateType: (dateType: PrimitiveType) => U,
timeType: (timeType: PrimitiveType) => U,
dateTimeType: (dateTimeType: PrimitiveType) => U
@ -239,7 +237,6 @@ export function matchTypeExhaustive<U>(
else if (t instanceof ObjectType) return objectType(t);
else if (t instanceof EnumType) return enumType(t);
else if (t instanceof UnionType) return unionType(t);
else if (t instanceof TransformedType) return transformedType(t);
return panic(`Unknown type ${t.kind}`);
}
@ -281,7 +278,6 @@ export function matchType<U>(
typeNotSupported,
enumType,
unionType,
typeNotSupported,
stringTypeMatchers.dateType || typeNotSupported,
stringTypeMatchers.timeType || typeNotSupported,
stringTypeMatchers.dateTimeType || typeNotSupported
@ -295,8 +291,7 @@ export function matchCompoundType(
classType: (classType: ClassType) => void,
mapType: (mapType: MapType) => void,
objectType: (objectType: ObjectType) => void,
unionType: (unionType: UnionType) => void,
transformedType: (transformedType: TransformedType) => void
unionType: (unionType: UnionType) => void
): void {
function ignore<T extends Type>(_: T): void {
return;
@ -317,7 +312,6 @@ export function matchCompoundType(
objectType,
ignore,
unionType,
transformedType,
ignore,
ignore,
ignore

Просмотреть файл

@ -112,9 +112,9 @@ export class UnifyUnionBuilder extends UnionBuilder<TypeBuilder & TypeLookerUp,
forwardingRef: TypeRef | undefined
): TypeRef {
if (this._makeEnums) {
return this.typeBuilder.getEnumType(typeAttributes, OrderedSet(enumCases), forwardingRef);
return this.typeBuilder.getEnumType(typeAttributes, OrderedSet(enumCases), undefined, forwardingRef);
} else {
return this.typeBuilder.getStringType(typeAttributes, OrderedMap(counts), forwardingRef);
return this.typeBuilder.getStringType(typeAttributes, OrderedMap(counts), undefined, forwardingRef);
}
}
@ -170,6 +170,7 @@ export class UnifyUnionBuilder extends UnionBuilder<TypeBuilder & TypeLookerUp,
typeAttributes,
properties,
additionalProperties,
undefined,
forwardingRef
);
} else {
@ -178,6 +179,7 @@ export class UnifyUnionBuilder extends UnionBuilder<TypeBuilder & TypeLookerUp,
typeAttributes,
this._makeClassesFixed,
properties,
undefined,
forwardingRef
);
}
@ -189,7 +191,7 @@ export class UnifyUnionBuilder extends UnionBuilder<TypeBuilder & TypeLookerUp,
typeAttributes: TypeAttributes,
forwardingRef: TypeRef | undefined
): TypeRef {
const ref = this.typeBuilder.getArrayType(this._unifyTypes(arrays, Map()), forwardingRef);
const ref = this.typeBuilder.getArrayType(this._unifyTypes(arrays, Map()), undefined, forwardingRef);
this.typeBuilder.addAttributes(ref, typeAttributes);
return ref;
}
@ -222,6 +224,7 @@ export function unifyTypes<T extends Type>(
conflateNumbers: boolean,
maybeForwardingRef?: TypeRef
): TypeRef {
assert(types.every(t => t.transformation === undefined), "We don't support unifying types with transformations yet");
if (types.isEmpty()) {
return panic("Cannot unify empty set of types");
} else if (types.count() === 1) {

Просмотреть файл

@ -233,6 +233,7 @@ export class TypeRefUnionAccumulator extends UnionAccumulator<TypeRef, TypeRef>
// There is a method analogous to this in the IntersectionAccumulator. It might
// make sense to find a common interface.
private addType(t: Type, attributes: TypeAttributes): void {
assert(t.transformation === undefined, "We don't support transformations in unions yet");
matchTypeExhaustive(
t,
_noneType => this.addNone(attributes),
@ -260,9 +261,6 @@ export class TypeRefUnionAccumulator extends UnionAccumulator<TypeRef, TypeRef>
_unionType => {
return panic("The unions should have been eliminated in attributesForTypesInUnion");
},
_transformedType => {
return panic("We don't support transformed types in unions yet");
},
_dateType => this.addStringType("date", attributes),
_timeType => this.addStringType("time", attributes),
_dateTimeType => this.addStringType("date-time", attributes)
@ -312,11 +310,11 @@ export abstract class UnionBuilder<TBuilder extends TypeBuilder, TArrayData, TOb
case "date":
case "time":
case "date-time":
const t = this.typeBuilder.getPrimitiveType(kind, forwardingRef);
const t = this.typeBuilder.getPrimitiveType(kind, undefined, forwardingRef);
this.typeBuilder.addAttributes(t, typeAttributes);
return t;
case "string":
return this.typeBuilder.getStringType(typeAttributes, undefined, forwardingRef);
return this.typeBuilder.getStringType(typeAttributes, undefined, undefined, forwardingRef);
case "enum":
return this.makeEnum(typeProvider.enumCases, typeProvider.enumCaseMap, typeAttributes, forwardingRef);
case "object":
@ -357,7 +355,7 @@ export abstract class UnionBuilder<TBuilder extends TypeBuilder, TArrayData, TOb
}
const union = unique
? this.typeBuilder.getUniqueUnionType(typeAttributes, undefined, forwardingRef)
? this.typeBuilder.getUniqueUnionType(typeAttributes, undefined, undefined, forwardingRef)
: undefined;
const types: TypeRef[] = [];
@ -369,7 +367,7 @@ export abstract class UnionBuilder<TBuilder extends TypeBuilder, TArrayData, TOb
this.typeBuilder.setSetOperationMembers(union, typesSet);
return union;
} else {
return this.typeBuilder.getUnionType(typeAttributes, typesSet, forwardingRef);
return this.typeBuilder.getUnionType(typeAttributes, typesSet, undefined, forwardingRef);
}
}
}