import { select } from '@inquirer/prompts'; import { ModulePoint } from "./lib/point.js"; import { DefaultMap, pretty, prettyT } from './util.js'; import { symbolFunction } from './structures/types.js'; import { ModuleStd } from './stdlib.js'; import { Type } from "./primitives/types.js"; import { assign, makeGeneric, unify } from './generics/generics.js'; class Context { constructor(mod) { this.mod = mod; this.types = new DefaultMap(() => new Set()); // instance to type this.instances = new DefaultMap(() => new Set()); // type to instance for (const {i, t} of mod.l) { this.types.getdefault(i, true).add(t); this.instances.getdefault(t, true).add(i); } this.functionsFrom = new DefaultMap(() => new Set()); // type to outgoing function this.functionsTo = new DefaultMap(() => new Set()); // type to incoming function for (const t of this.instances.m.keys()) { if (t.symbol === symbolFunction) { // 't' is a function signature for (const fn of this.instances.getdefault(t)) { this.functionsFrom.getdefault(t.params[0], true).add(fn); this.functionsTo .getdefault(t.params[1], true).add(fn); } } } // this.typeVarAssigns = new Map(); // for (const t of this.instances.m.keys()) { // if (t.typeVars) { // for (const t2 of this.instances.m.keys()) { // const genericT2 = (t2.typeVars === undefined) // ? makeGeneric(() => t2) // : t2; // try { // const unification = unify(t, t2); // console.log(unification); // } catch (e) { // // skip // } // } // } // } } addToCtx({i, t}) { return new Context({l:[ ...this.mod.l, {i, t}, ]}) } } let ctx = new Context({l:[ ...ModuleStd.l, ...ModulePoint.l, ]}); const prettyIT = ({i, t}) => ({ strI: isType(i) ? prettyT(i) : pretty(i), strT: prettyT(t), }) const toChoices = ([i, types]) => { return [...types].map(t => { const {strI, strT} = prettyIT({i, t}); return { value: {i, t}, name: strI, description: ` :: ${strT}`, short: `${strI} :: ${strT}`, }; }); } const isType = i => ctx.types.getdefault(i).has(Type); async function topPrompt() { const action = await select({ message: "What do you want to do?", choices: [ "list all types", "list all functions", "list all", ], }); if (action === "list all types") { await listInstances(Type); // await listAllTypes(); } if (action === "list all functions") { await listAllFunctions(); } if (action === "list all") { await listAllInstances(); } return topPrompt(); } // async function listAllTypes() { // const choice = await select({ // message: "select type:", // choices: [ // "(go back)", // ...[...ctx.instances.m.keys()].map(t => ({ // value: t, // name: prettyT(t), // })), // ] // }); // if (choice === "(go back)") { // return; // } // await typeOptions(choice); // return listAllTypes(); // } async function listAllFunctions() { const choice = await select({ message: "select function:", choices: [ "(go back)", ...[...ctx.types.m.entries()] .filter(([i, types]) => { for (const type of types) { if (type.symbol === symbolFunction) return true; } return false; }) .flatMap(toChoices), ], }); if (choice === "(go back)") { return; } const {i, t} = choice; await functionOptions(i, t); return listAllFunctions(); } async function typeOptions(t) { const choice = await select({ message: `actions for type ${prettyT(t)} :: Type`, choices: [ "(go back)", "list instances", // "list outgoing functions", // "list incoming functions", "print raw", "treat as instance", ], }); if (choice === "(go back)") { return; } else if (choice === "list instances") { await listInstances(t); } else if (choice === "print raw") { console.log(pretty(t)); } else if (choice === "treat as instance") { await instanceOptions(t, Type) } else { console.log("unimplemented:", choice); } return typeOptions(t); } async function listAllInstances() { const choice = await select({ message: `all instances:`, choices: [ "(go back)", ... [...ctx.types.m.keys()].flatMap(i => toChoices([i, ctx.types.getdefault(i)])), ], }) if (choice === "(go back)") { return; } return instanceOrTypeOrFnOptions(choice); } async function instanceOrTypeOrFnOptions({i, t}) { if (t.symbol === symbolFunction) { return functionOptions(i, t); } if (isType(i)) { return typeOptions(i); } return instanceOptions(i,t); } async function listInstances(t) { const choice = await select({ message: `instances of ${prettyT(t)}:`, choices: [ "(go back)", ... [...ctx.instances.getdefault(t)].flatMap(i => toChoices([i, [t]])), ], }); if (choice === "(go back)") { return; } return instanceOrTypeOrFnOptions(choice); } async function listTypes(i) { const {strI} = prettyIT({i,t:Type}); const choice = await select({ message: `type(s) of ${strI}:`, choices: [ "(go back)", ... [...ctx.types.getdefault(i)].flatMap(t => toChoices([t, ctx.types.getdefault(t)])), ], }); if (choice === "(go back)") { return; } const {i: chosenType} = choice; await typeOptions(chosenType); return listTypes(i); } async function functionOptions(fn, fnT) { const {strI, strT} = prettyIT({i: fn, t: fnT}); const choice = await select({ message: `actions for function ${strI} :: ${strT}`, choices: [ "(go back)", "call", "treat as instance", ], }); if (choice === "(go back)") { return; } if (choice === "call") { await callFunction(fn, fnT); } if (choice === "treat as instance") { await instanceOptions(fn, fnT); } return functionOptions(fn, fnT); } async function callFunction(fn, fnT) { const {strI, strT} = prettyIT({i: fn, t: fnT}); const inType = fnT.params[0]; const choice = await select({ message: `select parameter for function ${strI} :: ${strT}`, choices: [ "(go back)", ... [...ctx.instances.getdefault(inType)].flatMap(i => toChoices([i, ctx.types.getdefault(i)])), ], }); if (choice === "(go back)") { return; } const {i, t} = choice; await apply(i, fn, fnT); return callFunction(fn, fnT); } async function instanceOptions(i,t) { const {strI, strT} = prettyIT({i,t}); const choice = await select({ message: `actions for instance ${strI} :: ${strT}`, choices: [ "(go back)", "transform", "list type(s)", ] }) if (choice === "(go back)") { return; } if (choice === "transform") { await transform(i, t) } if (choice === "list type(s)") { await listTypes(i); } return await instanceOptions(i,t); } async function transform(i, t) { const {strI, strT} = prettyIT({i, t}); console.log(ctx.functionsFrom.getdefault(t)); const choice = await select({ message: `choose transformation to perform on ${strI} :: ${strT}`, choices: [ "(go back)", ... [...ctx.functionsFrom.getdefault(t)].flatMap(fn => toChoices([fn, ctx.types.getdefault(fn)])), ], }); if (choice === "(go back)") { return; } const {i:fn,t:fnT} = choice; await apply(i, fn, fnT); return transform(i, t); } async function apply(i, fn, fnT) { const result = fn(i); const resultType = fnT.params[1]; // update context with newly produced value ctx = ctx.addToCtx({i: result, t: resultType}); const {strI: strResult, strT: strResultType} = prettyIT({i: result, t: resultType}); console.log(`result = ${strResult} :: ${strResultType}`); return instanceOrTypeOrFnOptions({i: result, t: resultType}); } topPrompt();