import { number, select } from "@inquirer/prompts"; import { makeCompareFn } from "../../lib/compare/dynamic.js"; import { getFunctions, getInstances, getTypes, growEnv } from "../../lib/environment/env.js"; import { assignFn } from "../../lib/generics/generics.js"; import { getInst, getType, newDynamic } from "../../lib/primitives/dynamic.js"; import { Bool, Double, Int, SymbolDynamic, Type, UUID } from "../../lib/primitives/primitive_types.js"; import { eqType, getSymbol } from "../../lib/primitives/type.js"; import { ModuleStd } from "../../lib/stdlib.js"; import { fold as foldList } from "../../lib/structures/list.js"; import { 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"; const defaultSelectOptions = { pageSize: 13, } const proxyDynamic = async (env, dynamic) => { const newEnv = growEnv(env)(dynamic); const type = getType(dynamic) if (eqType(type)(Type)) { return listTypeOptions(newEnv, getInst(dynamic)); } if (getSymbol(type) === symbolFunction) { return listFunctionOptions(newEnv, dynamic); } if (getSymbol(type) === SymbolDynamic) { return proxyDynamic(newEnv, getInst(dynamic)); } return listInstanceOptions(newEnv, dynamic); } const selectInstance = async (env, type, msg) => { const instances = getInstances(env)(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; } export const listInstances = async (env, type) => { const choice = await selectInstance(env, type, "instances of"); if (choice === undefined) { return; } await proxyDynamic(env, newDynamic(choice)(type)); return await listInstances(env, type); } const listTypeOptions = async (env, type) => { const choice = await select({ message: `type ${pretty(type)}:`, choices: [ "(go back)", "new", "list instances", "treat as instance", ], ...defaultSelectOptions, }); if (choice === "(go back)") { return; } if (choice === "new") { const i = await createInstance(type); if (i !== undefined) { const dynamic = newDynamic(i)(type); await proxyDynamic(env, dynamic); } } if (choice === "list instances") { await listInstances(env, type); } if (choice === "treat as instance") { await listInstanceOptions(newDynamic(type)(Type)); } return await listTypeOptions(env, type); } const listInstanceOptions = async (env, dynamic) => { const choice = await select({ message: `instance ${pretty(dynamic)}:`, choices: [ "(go back)", "transform", "get type", ], ...defaultSelectOptions, }); if (choice === "(go back)") { return; } if (choice === "transform") { await transform(env, dynamic); } if (choice === "get type") { await listTypeOptions(env, getType(dynamic)); } return await listInstanceOptions(env, dynamic); } const listFunctionOptions = async (env, dynamic) => { const choice = await select({ message: `function ${pretty(dynamic)}:`, choices: [ "(go back)", "call", ], ...defaultSelectOptions, }); if (choice === "(go back)") { return; } if (choice === "call") { await call(env, 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'}, ], ...defaultSelectOptions, }); 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 (env, dynamic) => { const allFunctions = getFunctions(env); 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; } })([])(allFunctions); 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)}`, })), ], ...defaultSelectOptions, }); if (choice === "(go back)") { return; } const {fun, outType} = choice; const outValue = getInst(fun)(getInst(dynamic)); await proxyDynamic(env, newDynamic(outValue)(outType)); return transform(env, dynamic); } const call = async (env, funDynamic) => { const funType = getType(funDynamic); const allTypes = getTypes(env); // 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 assignable to ${pretty(funType.params[0](funType))}:`, choices: [ "(go back)", ...inTypes.map(({inType, outType}) => ({ value: {inType, outType}, name: pretty(inType), })), ], ...defaultSelectOptions, }); if (choice === "(go back)") { return; } const inValue = await selectInstance(env, choice.inType, "select input value of type"); if (inValue === undefined) { return; } const envWithInput = growEnv(env)(newDynamic(inValue)(choice.inType)); const outValue = getInst(funDynamic)(inValue); await proxyDynamic(envWithInput, newDynamic(outValue)(choice.outType)); return call(env, funDynamic); }