2021-03-02 21:43:10 +00:00
|
|
|
import { Record, Tuple } from "./record";
|
|
|
|
import { Bytes } from "./bytes";
|
|
|
|
import { Value } from "./values";
|
|
|
|
import { Set, Dictionary } from "./dictionary";
|
|
|
|
import { annotate, Annotated } from "./annotated";
|
|
|
|
import { Double, Float, Single } from "./float";
|
2021-02-17 19:55:22 +00:00
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
export type Fold<T, R = Value<T>> = (v: Value<T>) => R;
|
2021-02-17 19:55:22 +00:00
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
export interface FoldMethods<T, R> {
|
2021-02-17 19:55:22 +00:00
|
|
|
boolean(b: boolean): R;
|
|
|
|
single(f: number): R;
|
|
|
|
double(f: number): R;
|
|
|
|
integer(i: number): R;
|
|
|
|
string(s: string): R;
|
|
|
|
bytes(b: Bytes): R;
|
|
|
|
symbol(s: symbol): R;
|
|
|
|
|
2021-02-25 22:16:05 +00:00
|
|
|
record(r: Record<Value<T>, Tuple<Value<T>>, T>, k: Fold<T, R>): R;
|
2021-02-17 19:55:22 +00:00
|
|
|
array(a: Array<Value<T>>, k: Fold<T, R>): R;
|
|
|
|
set(s: Set<T>, k: Fold<T, R>): R;
|
|
|
|
dictionary(d: Dictionary<Value<T>, T>, k: Fold<T, R>): R;
|
|
|
|
|
|
|
|
annotated(a: Annotated<T>, k: Fold<T, R>): R;
|
|
|
|
|
|
|
|
pointer(t: T, k: Fold<T, R>): R;
|
|
|
|
}
|
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
export abstract class ValueFold<T, R = T> implements FoldMethods<T, Value<R>> {
|
2021-02-17 19:55:22 +00:00
|
|
|
boolean(b: boolean): Value<R> {
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
single(f: number): Value<R> {
|
|
|
|
return Single(f);
|
|
|
|
}
|
|
|
|
double(f: number): Value<R> {
|
|
|
|
return Double(f);
|
|
|
|
}
|
|
|
|
integer(i: number): Value<R> {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
string(s: string): Value<R> {
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
bytes(b: Bytes): Value<R> {
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
symbol(s: symbol): Value<R> {
|
|
|
|
return s;
|
|
|
|
}
|
2021-02-25 22:16:05 +00:00
|
|
|
record(r: Record<Value<T>, Tuple<Value<T>>, T>, k: Fold<T, Value<R>>): Value<R> {
|
2021-02-25 18:37:22 +00:00
|
|
|
return Record(k(r.label), r.map(k));
|
2021-02-17 19:55:22 +00:00
|
|
|
}
|
|
|
|
array(a: Value<T>[], k: Fold<T, Value<R>>): Value<R> {
|
|
|
|
return a.map(k);
|
|
|
|
}
|
|
|
|
set(s: Set<T>, k: Fold<T, Value<R>>): Value<R> {
|
|
|
|
return s.map(k);
|
|
|
|
}
|
|
|
|
dictionary(d: Dictionary<Value<T>, T>, k: Fold<T, Value<R>>): Value<R> {
|
|
|
|
return d.mapEntries(([key, value]) => [k(key), k(value)]);
|
|
|
|
}
|
|
|
|
annotated(a: Annotated<T>, k: Fold<T, Value<R>>): Value<R> {
|
|
|
|
return annotate(k(a.item), ...a.annotations.map(k));
|
|
|
|
}
|
|
|
|
abstract pointer(t: T, k: Fold<T, Value<R>>): Value<R>;
|
|
|
|
}
|
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
export class IdentityFold<T> extends ValueFold<T, T> {
|
2021-02-17 19:55:22 +00:00
|
|
|
pointer(t: T, _k: Fold<T, Value<T>>): Value<T> {
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
export class MapFold<T, R extends object> extends ValueFold<T, R> {
|
2021-02-24 19:40:49 +00:00
|
|
|
readonly f: (t: T) => Value<R>;
|
2021-02-17 19:55:22 +00:00
|
|
|
|
2021-02-24 19:40:49 +00:00
|
|
|
constructor(f: (t: T) => Value<R>) {
|
2021-02-17 19:55:22 +00:00
|
|
|
super();
|
|
|
|
this.f = f;
|
|
|
|
}
|
|
|
|
|
|
|
|
pointer(t: T, _k: Fold<T, Value<R>>): Value<R> {
|
|
|
|
return this.f(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const IDENTITY_FOLD = new IdentityFold();
|
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
export function fold<T, R>(v: Value<T>, o: FoldMethods<T, R>): R {
|
2021-02-17 19:55:22 +00:00
|
|
|
const walk = (v: Value<T>): R => {
|
|
|
|
switch (typeof v) {
|
|
|
|
case 'boolean':
|
|
|
|
return o.boolean(v);
|
|
|
|
case 'number':
|
|
|
|
if (!Number.isInteger(v)) {
|
|
|
|
// TODO: Is this convenience warranted?
|
|
|
|
return o.double(v);
|
|
|
|
} else {
|
|
|
|
return o.integer(v);
|
|
|
|
}
|
|
|
|
case 'string':
|
|
|
|
return o.string(v);
|
|
|
|
case 'symbol':
|
|
|
|
return o.symbol(v);
|
|
|
|
case 'object':
|
2021-02-25 22:16:05 +00:00
|
|
|
if (Record.isRecord<Value<T>, Tuple<Value<T>>, T>(v)) {
|
2021-02-17 19:55:22 +00:00
|
|
|
return o.record(v, walk);
|
|
|
|
} else if (Array.isArray(v)) {
|
|
|
|
return o.array(v, walk);
|
|
|
|
} else if (Set.isSet<T>(v)) {
|
|
|
|
return o.set(v, walk);
|
|
|
|
} else if (Dictionary.isDictionary<Value<T>, T>(v)) {
|
|
|
|
return o.dictionary(v, walk);
|
|
|
|
} else if (Annotated.isAnnotated<T>(v)) {
|
|
|
|
return o.annotated(v, walk);
|
|
|
|
} else if (Bytes.isBytes(v)) {
|
|
|
|
return o.bytes(v);
|
|
|
|
} else if (Float.isSingle(v)) {
|
|
|
|
return o.single(v.value);
|
|
|
|
} else if (Float.isDouble(v)) {
|
|
|
|
return o.double(v.value);
|
|
|
|
} else {
|
|
|
|
/* fall through */
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return o.pointer(v, walk);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return walk(v);
|
|
|
|
}
|
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
export function mapPointers<T, R extends object>(
|
2021-02-17 19:55:22 +00:00
|
|
|
v: Value<T>,
|
2021-02-24 19:40:49 +00:00
|
|
|
f: (t: T) => Value<R>,
|
2021-02-17 19:55:22 +00:00
|
|
|
): Value<R>
|
|
|
|
{
|
|
|
|
return fold(v, new MapFold(f));
|
|
|
|
}
|
2021-03-09 14:59:40 +00:00
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
export function isPointer<T>(v: Value<T>): v is T {
|
2021-03-09 14:59:40 +00:00
|
|
|
return fold(v, {
|
|
|
|
boolean(_b: boolean): boolean { return false; },
|
|
|
|
single(_f: number): boolean { return false; },
|
|
|
|
double(_f: number): boolean { return false; },
|
|
|
|
integer(_i: number): boolean { return false; },
|
|
|
|
string(_s: string): boolean { return false; },
|
|
|
|
bytes(_b: Bytes): boolean { return false; },
|
|
|
|
symbol(_s: symbol): boolean { return false; },
|
|
|
|
|
|
|
|
record(_r: Record<Value<T>, Tuple<Value<T>>, T>, _k: Fold<T, boolean>): boolean {
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
array(_a: Array<Value<T>>, _k: Fold<T, boolean>): boolean { return false; },
|
|
|
|
set(_s: Set<T>, _k: Fold<T, boolean>): boolean { return false; },
|
|
|
|
dictionary(_d: Dictionary<Value<T>, T>, _k: Fold<T, boolean>): boolean {
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
|
|
annotated(_a: Annotated<T>, _k: Fold<T, boolean>): boolean { return false; },
|
|
|
|
|
|
|
|
pointer(_t: T, _k: Fold<T, boolean>): boolean { return true; },
|
|
|
|
});
|
|
|
|
}
|