265 lines
6.8 KiB
JavaScript
265 lines
6.8 KiB
JavaScript
import { number, select } from "@inquirer/prompts";
|
|
|
|
import { makeCompareFn } from "../lib/compare/dynamic.js";
|
|
import { compareTypes } from "../lib/compare/type.js";
|
|
import { getInst, getType, newDynamic } from "../lib/primitives/dynamic.js";
|
|
import { Bool, Double, Int, Type, UUID } from "../lib/primitives/primitive_types.js";
|
|
import { eqType, getSymbol } from "../lib/primitives/type.js";
|
|
import { ModuleStd } from "../lib/stdlib.js";
|
|
import { emptyDict, get, set, fold as foldDict } from "../lib/structures/dict.js";
|
|
import { fold as foldList } from "../lib/structures/list.js";
|
|
import { add, emptySet, fold as foldSet } from "../lib/structures/set.js";
|
|
import { symbolFunction } from "../lib/structures/type_constructors.js";
|
|
import { pretty, prettyT } from "../lib/util/pretty.js";
|
|
import { genUUID } from "../lib/util/random.js";
|
|
import { assignFn, unify } from "../lib/generics/generics.js";
|
|
|
|
// console.log(ModuleStd);
|
|
|
|
const addEntry = dict => i => t => {
|
|
const setOfInstances = get(dict)(t) || emptySet(makeCompareFn(t));
|
|
return set(dict)(t)(add(setOfInstances)(i));
|
|
};
|
|
|
|
const typeDict = foldList(typeDict => dynamic => {
|
|
try {
|
|
const t = getType(dynamic);
|
|
// add instance to type:
|
|
return addEntry(
|
|
addEntry(typeDict)(getInst(dynamic))(t)
|
|
)(t)(Type);
|
|
} catch (e) {
|
|
console.log('warning:', e.message);
|
|
return typeDict;
|
|
}
|
|
})(emptyDict(compareTypes))(ModuleStd);
|
|
|
|
const functions = foldList(functions => dynamic => {
|
|
if (getSymbol(getType(dynamic)) === symbolFunction) {
|
|
return [...functions, dynamic];
|
|
}
|
|
return functions;
|
|
})([])(ModuleStd);
|
|
|
|
// console.log(typeDict);
|
|
// console.log(functions);
|
|
|
|
const defaultSelectOptions = {
|
|
pageSize: 20,
|
|
}
|
|
|
|
const selectInstance = async (type, msg) => {
|
|
const instances = get(typeDict)(type) || emptySet(makeCompareFn(type));
|
|
const choices = foldSet(acc => instance => {
|
|
return [...acc, {
|
|
value: instance,
|
|
name: pretty(instance),
|
|
}];
|
|
})(["(go back)", "(new)"])(instances);
|
|
const choice = await select({
|
|
message: `${msg} ${pretty(type)}:`,
|
|
choices,
|
|
...defaultSelectOptions,
|
|
});
|
|
if (choice === "(go back)") {
|
|
return;
|
|
}
|
|
if (choice === "(new)") {
|
|
return createInstance(type);
|
|
}
|
|
return choice;
|
|
}
|
|
|
|
const listInstances = async type => {
|
|
const choice = await selectInstance(type, "instances of");
|
|
if (choice === undefined) {
|
|
return;
|
|
}
|
|
await proxyDynamic(newDynamic(choice)(type));
|
|
return await listInstances(type);
|
|
}
|
|
|
|
const proxyDynamic = async dynamic => {
|
|
const type = getType(dynamic)
|
|
if (eqType(type)(Type)) {
|
|
return listTypeOptions(getInst(dynamic));
|
|
}
|
|
if (getSymbol(type) === symbolFunction) {
|
|
return listFunctionOptions(dynamic);
|
|
}
|
|
return listInstanceOptions(dynamic);
|
|
}
|
|
|
|
const listTypeOptions = async type => {
|
|
const choice = await select({
|
|
message: `type ${pretty(type)}:`,
|
|
choices: [
|
|
"(go back)",
|
|
"new",
|
|
"list instances",
|
|
"treat as instance",
|
|
],
|
|
});
|
|
if (choice === "(go back)") {
|
|
return;
|
|
}
|
|
if (choice === "new") {
|
|
const i = await createInstance(type);
|
|
if (i !== undefined) {
|
|
const dynamic = newDynamic(i)(type);
|
|
await proxyDynamic(dynamic);
|
|
}
|
|
}
|
|
if (choice === "list instances") {
|
|
await listInstances(type);
|
|
}
|
|
if (choice === "treat as instance") {
|
|
await listInstanceOptions(newDynamic(type)(Type));
|
|
}
|
|
return await listTypeOptions(type);
|
|
}
|
|
|
|
const listInstanceOptions = async dynamic => {
|
|
const choice = await select({
|
|
message: `instance ${pretty(dynamic)}:`,
|
|
choices: [
|
|
"(go back)",
|
|
"transform",
|
|
"get type",
|
|
],
|
|
});
|
|
if (choice === "(go back)") {
|
|
return;
|
|
}
|
|
if (choice === "transform") {
|
|
await transform(dynamic);
|
|
}
|
|
if (choice === "get type") {
|
|
await listTypeOptions(getType(dynamic));
|
|
}
|
|
return await listInstanceOptions(dynamic);
|
|
}
|
|
|
|
const listFunctionOptions = async dynamic => {
|
|
const choice = await select({
|
|
message: `function ${pretty(dynamic)}:`,
|
|
choices: [
|
|
"(go back)",
|
|
"call",
|
|
],
|
|
});
|
|
if (choice === "(go back)") {
|
|
return;
|
|
}
|
|
if (choice === "call") {
|
|
await call(dynamic);
|
|
}
|
|
}
|
|
|
|
const createInstance = async type => {
|
|
if (eqType(type)(Int)) {
|
|
const n = await number({
|
|
message: `enter an integer (leave empty to go back):`,
|
|
step: 1, // only integers
|
|
});
|
|
if (n === undefined) {
|
|
return;
|
|
}
|
|
return BigInt(n);
|
|
}
|
|
if (eqType(type)(Double)) {
|
|
const n = await number({
|
|
message: `enter a number (leave empty to go back):`,
|
|
step: 'any',
|
|
});
|
|
return n;
|
|
}
|
|
if (eqType(type)(Bool)) {
|
|
const b = await select({
|
|
message: `select:`,
|
|
choices: [
|
|
{value: false, name: 'false'},
|
|
{value: true, name: 'true'},
|
|
],
|
|
});
|
|
return b;
|
|
}
|
|
if (eqType(type)(UUID)) {
|
|
const descr = await input({message: "enter UUID description:"});
|
|
return descr + '__' + genUUID(16);
|
|
}
|
|
console.log("no prompt handler for creating new", prettyT(type));
|
|
};
|
|
|
|
const transform = async dynamic => {
|
|
const enabled = foldList(enabled => fun => {
|
|
try {
|
|
const outType = assignFn(getType(fun), getType(dynamic));
|
|
return [...enabled, {fun, outType}];
|
|
} catch (e) {
|
|
// console.log('warning:', e);
|
|
return enabled;
|
|
}
|
|
})([])(functions);
|
|
|
|
const choice = await select({
|
|
message: `select:`,
|
|
choices: [
|
|
"(go back)",
|
|
...enabled.map(({fun, outType}) => ({
|
|
value: {fun, outType},
|
|
name: `${getInst(fun).name} ${pretty(getInst(dynamic))} :: ${pretty(outType)}`,
|
|
})),
|
|
],
|
|
});
|
|
|
|
if (choice === "(go back)") {
|
|
return;
|
|
}
|
|
const {fun, outType} = choice;
|
|
const outValue = getInst(fun)(getInst(dynamic));
|
|
await proxyDynamic(newDynamic(outValue)(outType));
|
|
return transform(dynamic);
|
|
}
|
|
|
|
const call = async dynamic => {
|
|
const funType = getType(dynamic);
|
|
const allTypes = get(typeDict)(Type);
|
|
|
|
// compatible input types
|
|
const inTypes = foldSet(types => inType => {
|
|
try {
|
|
const outType = assignFn(funType, inType); // may throw
|
|
return [...types, {inType, outType}];
|
|
} catch (e) {
|
|
return types;
|
|
}
|
|
})([])(allTypes);
|
|
|
|
const choice = inTypes.length === 1
|
|
? inTypes[0]
|
|
: await select({
|
|
message: `types servable as input to ${pretty(funType)}:`,
|
|
choices: [
|
|
"(go back)",
|
|
...inTypes.map(({inType, outType}) => ({
|
|
value: {inType, outType},
|
|
name: pretty(inType),
|
|
})),
|
|
],
|
|
});
|
|
|
|
if (choice === "(go back)") {
|
|
return;
|
|
}
|
|
const inValue = await selectInstance(choice.inType, "select input value of type");
|
|
if (inValue === undefined) {
|
|
return;
|
|
}
|
|
const outValue = getInst(dynamic)(inValue);
|
|
await proxyDynamic(newDynamic(outValue)(choice.outType));
|
|
return call(dynamic);
|
|
}
|
|
|
|
await listInstances(Type);
|
|
// await transform(newDynamic(5)(Double));
|