import { number, select } from "@inquirer/prompts"; import { makeCompareFn } from "../lib/compare/dynamic.js"; import { compareTypes } from "../lib/compare/type.js"; import { assignFn } from "../lib/generics/generics.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 } 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"; // 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));