import { useState } from "react"; import { apply, UnifyError, assignFn, getType, getSymbol, symbolFunction } from "dope2"; import { Editor, type EditorState } from "./Editor"; import { Value } from "./Value"; import type { Dynamic, State2Props } from "./util/extra"; import "./CallBlock.css"; export interface CallBlockState< FnState=EditorState, InputState=EditorState, > { kind: "call"; env: any; fn: FnState; input: InputState; resolved: undefined | Dynamic; // focus: boolean; } interface CallBlockProps< FnState=EditorState, InputState=EditorState, > extends State2Props> { onResolve: (resolved: EditorState) => void; } function headlessCallBlock({state, setState, onResolve}: CallBlockProps) { const [unifyError, setUnifyError] = useState(undefined); const {fn, input } = state; const setFn = (fn: EditorState) => { setState({...state, fn}); } const setInput = (input: EditorState) => { setState({...state, input}); } const setResolved = (resolved?: Dynamic) => { setState({...state, resolved}); } const makeTheCall = (input, fn) => { try { const outputResolved = apply(input.resolved)(fn.resolved); setResolved(outputResolved); onResolve({ ...state, resolved: outputResolved }); setUnifyError(undefined); } catch (e) { if (!(e instanceof UnifyError)) { throw e; } setUnifyError(e); onResolve({ ...state, resolved: undefined }) } }; const onFnResolve = (fnState) => { if (input.resolved) { makeTheCall(input, fnState); } else { // setFn(fnState); setResolved(undefined); onResolve({ ...state, resolved: undefined }); } }; const onInputResolve = (inputState) => { if (fn.resolved) { makeTheCall(inputState, fn); } else { setResolved(undefined); onResolve({ ...state, resolved: undefined }); } }; const onFnCancel = () => { setState(input); } const onInputCancel = () => { setState(fn); } return {unifyError, setFn, setInput, onFnResolve, onInputResolve, onFnCancel, onInputCancel}; } export function CallBlock({ state, setState, onResolve }: CallBlockProps) { const {unifyError, setFn, setInput, onFnResolve, onInputResolve, onInputCancel} = headlessCallBlock({ state, setState, onResolve }); return
{/* Sequence of input parameters */} {/* Output (or Error) */} {state.resolved && } {unifyError && unifyError.toString()}
; } function FunctionHeader({ fn, setFn, input, onFnResolve }) { if (fn.kind === "call") { // if the function we're calling is itself the result of a function call, // then we are anonymous, and so we don't draw a function name // recurse: const { onFnResolve : onFnFnResolve } = headlessCallBlock({state: fn, setState: setFn, onResolve: onFnResolve}); return setFn({...fn, fn: fnFn})} onFnResolve={onFnFnResolve} input={fn.input} />; } else { const filterCompatibleFns = ([_name, dynamic]: [string, Dynamic]) => { if (input.resolved) { try { const type = getType(dynamic); if (getSymbol(type) !== symbolFunction) { return false; } assignFn(type, getType(input.resolved)); } catch (e) { if (!(e instanceof UnifyError)) { throw e; } return false; } } return true; } // end of recursion - draw function name return
𝑓𝑛  {/*todo*/}} filter={filterCompatibleFns} />
; } } function InputParams({ fn, setFn, input, setInput, onFnResolve, onInputResolve, onInputCancel }) { const filterCompatibleInputs = ([_name, dynamic]: [string, Dynamic]) => { if (fn.resolved) { try { assignFn(getType(fn.resolved), getType(dynamic)); } catch (e) { if (!(e instanceof UnifyError)) { throw e; } return false; } } return true; } return
{(fn.kind === "call") && // if the function we're calling is itself the result of a function call, // then we render its input parameter nested in our own input parameter box, which is way more readable // Input(s) of the function we're calling: } {/* Our own input */}
; } function NestedInputParams({fn, setFn, onFnResolve}) { const { onInputResolve: onFnInputResolve, onFnResolve : onFnFnResolve, } = headlessCallBlock({state: fn, setState: setFn, onResolve: onFnResolve}); return setFn({...fn, fn: fnFn})} input={fn.input} setInput={fnInput => setFn({...fn, input: fnInput})} onFnResolve={onFnFnResolve} onInputResolve={onFnInputResolve} onInputCancel={() => {/*todo*/}} />; }