split 'environment' example up into library and interactive prompt

This commit is contained in:
Joeri Exelmans 2025-05-09 16:05:12 +02:00
parent 255bc475d7
commit 77dfc8b182
3 changed files with 308 additions and 261 deletions

View file

@ -1,265 +1,9 @@
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 { module2Env } from "../lib/environment/env.js";
import { Dynamic } from "../lib/primitives/primitive_types.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";
import { listInstances } from "./prompt/prompt.js";
// console.log(ModuleStd);
const typeDict = module2Env(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));
await listInstances(typeDict, Dynamic);

243
examples/prompt/prompt.js Normal file
View file

@ -0,0 +1,243 @@
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);
}