136 lines
4.8 KiB
TypeScript
136 lines
4.8 KiB
TypeScript
import { useContext } from "react";
|
|
|
|
import { EnvContext } from "./EnvContext";
|
|
// import { addFocusRightMost, evalCallBlock2, evalExprBlock, recomputeTypeVarsForEnv, scoreResolved, type Environment, type ResolvedType } from "./eval";
|
|
import { ExprBlock, type ExprBlockState, type SetStateFn, type State2Props } from "./ExprBlock";
|
|
import { GlobalContext } from "./GlobalContext";
|
|
import { Value } from "./Value";
|
|
|
|
import { getActions } from "./actions";
|
|
import "./CallBlock.css";
|
|
import { CallContext } from "./CallContext";
|
|
import { inferType, inferTypeCall, type Environment } from "./infer_type";
|
|
import { Type } from "./Type";
|
|
|
|
export interface CallBlockState {
|
|
kind: "call";
|
|
fn: ExprBlockState;
|
|
input: ExprBlockState;
|
|
}
|
|
|
|
export interface CallBlockProps<
|
|
FnState=ExprBlockState,
|
|
InputState=ExprBlockState,
|
|
> extends State2Props<CallBlockState,ExprBlockState> {}
|
|
|
|
function nestedFnProperties({state, setState, score}: CallBlockProps, env: Environment) {
|
|
const setFn = (callback: SetStateFn) => {
|
|
setState(state => ({...state, fn: callback(state.fn)}));
|
|
};
|
|
const onFnCancel = () => {
|
|
setState(state => state.input); // we become our input
|
|
};
|
|
const scoreFn = (fnSuggestion: ExprBlockState) => {
|
|
return score({
|
|
kind: "call",
|
|
fn: fnSuggestion,
|
|
input: state.input,
|
|
});
|
|
};
|
|
return {state: state.fn, setState: setFn, onCancel: onFnCancel, score: scoreFn};
|
|
}
|
|
|
|
function nestedInputProperties({state, setState, score}: CallBlockProps, env: Environment) {
|
|
const setInput = (callback: SetStateFn) => {
|
|
setState(state => ({...state, input: callback(state.input)}));
|
|
};
|
|
const onInputCancel = () => {
|
|
setState(state => /*addFocusRightMost*/(state.fn)); // we become our function
|
|
};
|
|
const scoreInput = (inputSuggestion: ExprBlockState) => {
|
|
return score({
|
|
kind: "call",
|
|
fn: state.fn,
|
|
input: inputSuggestion,
|
|
});
|
|
};
|
|
return {state: state.input, setState: setInput, onCancel: onInputCancel, score: scoreInput};
|
|
}
|
|
|
|
// function computePriority(fn: ResolvedType, input: ResolvedType, outPriority: (s: ResolvedType) => number, env) {
|
|
// // dirty, but works:
|
|
// const [fnR, env2] = recomputeTypeVarsForEnv('<fn>', fn, env);
|
|
// const [inR, env3] = recomputeTypeVarsForEnv('<in>', input, env2);
|
|
// const [resolved] = evalCallBlock2(fnR, inR, env3);
|
|
// const score = scoreResolved(resolved, outPriority);
|
|
// return score;
|
|
// }
|
|
|
|
export function CallBlock(props: CallBlockProps) {
|
|
const env = useContext(EnvContext);
|
|
const globalContext = useContext(GlobalContext);
|
|
const addParam = getActions(globalContext, props.setState).c;
|
|
// const [resolved] = evalExprBlock(props.state, env);
|
|
// return <span className={"functionBlock" + ((resolved.kind === "error") ? " unifyError" : "")}>
|
|
const typeInfo = inferTypeCall(props.state, env);
|
|
return <span className={"functionBlock"}>
|
|
<CallContext value={{addParam}}>
|
|
<FunctionHeader {...props} addParam={addParam} />
|
|
<div className="functionParams">
|
|
<div className="outputParam">
|
|
{/* Sequence of input parameters */}
|
|
<InputParams
|
|
{...props}
|
|
depth={0}
|
|
// errorDepth={(resolved.kind === "error") ? (resolved.depth) : -1}
|
|
errorDepth={-1}
|
|
addParam={addParam}
|
|
/>
|
|
{/* { (resolved.kind === "error") && resolved.e.toString()
|
|
|| (resolved.kind === "value") && <Value dynamic={resolved} />
|
|
|| "unknown" } */}
|
|
:: <Type type={typeInfo.type} />
|
|
</div>
|
|
</div>
|
|
</CallContext>
|
|
</span>;
|
|
}
|
|
|
|
function FunctionHeader(props) {
|
|
const env = useContext(EnvContext);
|
|
const globalContext = useContext(GlobalContext);
|
|
const nestedProperties = nestedFnProperties(props, env);
|
|
if (props.state.fn.kind === "call" && globalContext?.syntacticSugar) {
|
|
// 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
|
|
return <FunctionHeader {...nestedProperties} />;
|
|
}
|
|
else {
|
|
// end of recursion - draw function name
|
|
return <span className="functionName">
|
|
𝑓𝑛
|
|
<ExprBlock {...nestedProperties} />
|
|
</span>;
|
|
}
|
|
}
|
|
|
|
function InputParams({ depth, errorDepth, ...rest }) {
|
|
const env = useContext(EnvContext);
|
|
const globalContext = useContext(GlobalContext);
|
|
const isOffending = depth === errorDepth;
|
|
return <div className={"inputParam" + (isOffending ? " offending" : "")}>
|
|
{rest.state.fn.kind === "call"
|
|
&& globalContext?.syntacticSugar
|
|
&& <InputParams
|
|
{...nestedFnProperties(rest as CallBlockProps, env)}
|
|
depth={depth+1}
|
|
errorDepth={errorDepth}
|
|
/>}
|
|
{/* Our own input param */}
|
|
<EnvContext value={inferTypeCall(rest.state, env).inputEnv}>
|
|
<ExprBlock
|
|
{...nestedInputProperties(rest as CallBlockProps, env)}
|
|
/>
|
|
</EnvContext>
|
|
</div>;
|
|
}
|