This commit is contained in:
Mark Probst 2018-04-16 20:21:33 -07:00
Родитель a035363cfc
Коммит 919da6625e
2 изменённых файлов: 82 добавлений и 35 удалений

19
src/Tuple.ts Normal file
Просмотреть файл

@ -0,0 +1,19 @@
import { hash, is } from "immutable";
import { hashCodeInit, addHashCode } from "./Support";
export class Tuple<T, U> {
constructor(readonly first: T, readonly second: U) {}
hashCode(): number {
let hashCode = hashCodeInit;
hashCode = addHashCode(hashCode, hash(this.first));
hashCode = addHashCode(hashCode, hash(this.second));
return hashCode;
}
equals(other: any): boolean {
if (!(other instanceof Tuple)) return false;
return is(this.first, other.first) && is(this.second, other.second);
}
}

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

@ -2,7 +2,7 @@
import { Map, Set, OrderedMap, OrderedSet } from "immutable";
import { TypeKind, PrimitiveStringTypeKind, Type, UnionType } from "./Type";
import { TypeKind, Type, UnionType, Transformation } from "./Type";
import { matchTypeExhaustive } from "./TypeUtils";
import {
TypeAttributes,
@ -31,7 +31,7 @@ export interface UnionTypeProvider<TArrayData, TObjectData> {
readonly lostTypeAttributes: boolean;
}
export type TypeAttributeMap<T extends TypeKind> = OrderedMap<T, TypeAttributes>;
export type TypeAttributeMap<T> = OrderedMap<T, TypeAttributes>;
function addAttributes(
accumulatorAttributes: TypeAttributes | undefined,
@ -41,23 +41,26 @@ function addAttributes(
return combineTypeAttributes(accumulatorAttributes, newAttributes);
}
function setAttributes<T extends TypeKind>(
function setAttributes<T>(
attributeMap: TypeAttributeMap<T>,
kind: T,
key: T,
newAttributes: TypeAttributes
): TypeAttributeMap<T> {
return attributeMap.set(kind, addAttributes(attributeMap.get(kind), newAttributes));
return attributeMap.set(key, addAttributes(attributeMap.get(key), newAttributes));
}
function moveAttributes<T extends TypeKind>(map: TypeAttributeMap<T>, fromKind: T, toKind: T): TypeAttributeMap<T> {
const fromAttributes = defined(map.get(fromKind));
map = map.remove(fromKind);
return setAttributes(map, toKind, fromAttributes);
function moveAttributes<T>(map: TypeAttributeMap<T>, fromKey: T, toKey: T): TypeAttributeMap<T> {
const fromAttributes = defined(map.get(fromKey));
map = map.remove(fromKey);
return setAttributes(map, toKey, fromAttributes);
}
export class UnionAccumulator<TArray, TObject> implements UnionTypeProvider<TArray[], TObject[]> {
private _nonStringTypeAttributes: TypeAttributeMap<TypeKind> = OrderedMap();
private _stringTypeAttributes: TypeAttributeMap<PrimitiveStringTypeKind | "enum"> = OrderedMap();
// Once enums are not allowed anymore, this goes to undefined.
// Invariant: _enumTypeAttributes === undefined || _stringTypeAttributes.isEmpty()
private _enumTypeAttributes: TypeAttributes | undefined = Map();
private _stringTypeAttributes: TypeAttributeMap<Transformation | undefined> = OrderedMap();
readonly arrayData: TArray[] = [];
readonly objectData: TObject[] = [];
@ -71,9 +74,15 @@ export class UnionAccumulator<TArray, TObject> implements UnionTypeProvider<TArr
constructor(private readonly _conflateNumbers: boolean) {}
private have(kind: TypeKind): boolean {
return (
this._nonStringTypeAttributes.has(kind) || this._stringTypeAttributes.has(kind as PrimitiveStringTypeKind)
);
if (kind === "string") {
return !this._stringTypeAttributes.isEmpty();
} else if (kind === "enum") {
return this._enumTypeAttributes !== undefined;
}
if (this._nonStringTypeAttributes.has(kind)) {
return true;
}
return false;
}
get haveString(): boolean {
@ -103,22 +112,15 @@ export class UnionAccumulator<TArray, TObject> implements UnionTypeProvider<TArr
this._nonStringTypeAttributes = setAttributes(this._nonStringTypeAttributes, "double", attributes);
}
addStringType(kind: PrimitiveStringTypeKind, attributes: TypeAttributes): void {
if (this.have(kind)) {
this._stringTypeAttributes = setAttributes(this._stringTypeAttributes, kind, attributes);
return;
}
// string overrides all other string types, as well as enum
if (kind === "string") {
const oldAttributes = combineTypeAttributes(this._stringTypeAttributes.valueSeq().toArray());
const newAttributes = addAttributes(oldAttributes, attributes);
this._stringTypeAttributes = this._stringTypeAttributes.clear().set(kind, newAttributes);
this.enumCaseMap = {};
this.enumCases = [];
} else {
this._stringTypeAttributes = setAttributes(this._stringTypeAttributes, kind, attributes);
addStringType(transformation: Transformation | undefined, attributes: TypeAttributes): void {
if (this._enumTypeAttributes !== undefined) {
attributes = combineTypeAttributes(this._enumTypeAttributes, attributes);
}
this._stringTypeAttributes = setAttributes(this._stringTypeAttributes, transformation, attributes);
// string overrides enum
this._enumTypeAttributes = undefined;
this.enumCaseMap = {};
this.enumCases = [];
}
addArray(t: TArray, attributes: TypeAttributes): void {
this.arrayData.push(t);
@ -131,9 +133,12 @@ export class UnionAccumulator<TArray, TObject> implements UnionTypeProvider<TArr
addEnumCases(cases: OrderedMap<string, number>, attributes: TypeAttributes): void {
if (this.have("string")) {
this.addStringType("string", attributes);
this.addStringType(undefined, attributes);
return;
}
if (this._enumTypeAttributes === undefined) {
return panic("State machine screwed up");
}
cases.forEach((count, s) => {
if (!Object.prototype.hasOwnProperty.call(this.enumCaseMap, s)) {
@ -143,23 +148,46 @@ export class UnionAccumulator<TArray, TObject> implements UnionTypeProvider<TArr
this.enumCaseMap[s] += count;
});
this._stringTypeAttributes = setAttributes(this._stringTypeAttributes, "enum", attributes);
this._enumTypeAttributes = combineTypeAttributes(this._enumTypeAttributes, attributes);
}
addEnumCase(s: string, count: number, attributes: TypeAttributes): void {
this.addEnumCases(OrderedMap([[s, count] as [string, number]]), attributes);
}
getMemberKinds(): TypeAttributeMap<TypeKind> {
let merged = this._nonStringTypeAttributes.merge(this._stringTypeAttributes);
getMemberKinds(): OrderedMap<TypeKind, [Transformation | undefined, TypeAttributes]> {
let merged = this._nonStringTypeAttributes.map(ta => [undefined, ta] as [Transformation | undefined, TypeAttributes]);
if (this._enumTypeAttributes !== undefined) {
assert(this._stringTypeAttributes.isEmpty(), "State machine screwed up");
if (this.enumCases.length > 0) {
merged = merged.set("enum", [undefined, this._enumTypeAttributes]);
} else {
assert(this._enumTypeAttributes.size === 0, "How do we have enum type attributes but no cases?");
}
}
if (this._stringTypeAttributes.size > 0) {
assert(this._enumTypeAttributes === undefined, "State machine screwed up");
const combinedAttributes = combineTypeAttributes(this._stringTypeAttributes.valueSeq().toArray());
const transformations = this._stringTypeAttributes.keySeq().toSet();
let transformation: Transformation | undefined;
if (transformations.has(undefined)) {
transformation = undefined;
} else if (transformations.size === 1) {
transformation = defined(transformations.first());
} else {
transformation = unionOfTransformations("string", transformations);
}
merged = merged.set("string", [transformation, combinedAttributes]);
}
if (merged.isEmpty()) {
return OrderedMap([["none", Map()] as [TypeKind, TypeAttributes]]);
return OrderedMap([["none", [undefined, Map()]] as [TypeKind, [Transformation | undefined, TypeAttributes]]]);
}
if (this._nonStringTypeAttributes.has("any")) {
assert(this._lostTypeAttributes, "This had to be set when we added 'any'");
const allAttributes = combineTypeAttributes(merged.valueSeq().toArray());
return OrderedMap([["any", allAttributes] as [TypeKind, TypeAttributes]]);
const allAttributes = combineTypeAttributes(merged.valueSeq().toArray().map(([_, ta]) => ta));
return OrderedMap([["any", [undefined, allAttributes]] as [TypeKind, [Transformation | undefined, TypeAttributes]]]);
}
if (this._conflateNumbers && this.have("integer") && this.have("double")) {