diff --git a/src/component/app/App.tsx b/src/component/app/App.tsx index 1d43adc..4572456 100644 --- a/src/component/app/App.tsx +++ b/src/component/app/App.tsx @@ -1,11 +1,10 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { extendedEnv } from '../../context/EnvContext'; import { GlobalContext } from '../../context/GlobalContext'; import { inferType, scoreTypeInfo } from '../../eval/infer_type'; import { ExprBlock, type ExprBlockState } from '../expr/ExprBlock'; import { actionShortcuts } from './actions'; import { biggerExample, emptySet, factorial, higherOrder, higherOrder2Params, inc, initialEditorState, lambda2Params, nonEmptyEditorState, pushBool, setOfListOfBool, tripleFunctionCallEditorState } from "./configurations"; -// import { scoreResolved, type ResolvedType } from './eval'; import './App.css'; @@ -133,6 +132,11 @@ export function App() { } } + const currentState = appState.history.at(-1)!; + + // infer types for entire app state + const typeInfo = useMemo(() => inferType(currentState, extendedEnv), [currentState]); + return ( <>
@@ -168,13 +172,14 @@ export function App() {
{}} score={(state: ExprBlockState) => { const typeInfo = inferType(state, extendedEnv); return scoreTypeInfo(typeInfo); }} + typeInfo={typeInfo} />
diff --git a/src/component/expr/CallBlock.tsx b/src/component/expr/CallBlock.tsx index e7d195c..7d89673 100644 --- a/src/component/expr/CallBlock.tsx +++ b/src/component/expr/CallBlock.tsx @@ -1,14 +1,13 @@ import { useContext } from "react"; -import { EnvContext } from "../../context/EnvContext"; -import { ExprBlock, type ExprBlockState, type SetStateFn, type State2Props } from "./ExprBlock"; -import { GlobalContext } from "../../context/GlobalContext"; - -import { getActions } from "../app/actions"; -import "./CallBlock.css"; import { CallContext } from "../../context/CallContext"; -import { inferTypeCall, type Environment } from "../../eval/infer_type"; +import { EnvContext } from "../../context/EnvContext"; +import { GlobalContext } from "../../context/GlobalContext"; +import { type Environment, type TypeInfoCall } from "../../eval/infer_type"; +import { getActions } from "../app/actions"; import { Type } from "../other/Type"; +import "./CallBlock.css"; +import { ExprBlock, type ExprBlockState, type SetStateFn, type State2Props } from "./ExprBlock"; export interface CallBlockState { kind: "call"; @@ -19,9 +18,11 @@ export interface CallBlockState { export interface CallBlockProps< FnState=ExprBlockState, InputState=ExprBlockState, -> extends State2Props {} +> extends State2Props { + typeInfo: TypeInfoCall; +} -function nestedFnProperties({state, setState, score}: CallBlockProps, env: Environment) { +function nestedFnProperties({state, setState, score, typeInfo}: CallBlockProps, env: Environment) { const setFn = (callback: SetStateFn) => { setState(state => ({...state, fn: callback(state.fn)})); }; @@ -31,10 +32,10 @@ function nestedFnProperties({state, setState, score}: CallBlockProps, env: Envir const scoreFn = (fnSuggestion: ExprBlockState) => { return score({ ...state, fn: fnSuggestion }); }; - return {state: state.fn, setState: setFn, onCancel: onFnCancel, score: scoreFn}; + return {state: state.fn, setState: setFn, onCancel: onFnCancel, score: scoreFn, typeInfo: typeInfo.fn}; } -function nestedInputProperties({state, setState, score}: CallBlockProps, env: Environment) { +function nestedInputProperties({state, setState, score, typeInfo}: CallBlockProps, env: Environment) { const setInput = (callback: SetStateFn) => { setState(state => ({...state, input: callback(state.input)})); }; @@ -44,7 +45,7 @@ function nestedInputProperties({state, setState, score}: CallBlockProps, env: En const scoreInput = (inputSuggestion: ExprBlockState) => { return score({ ...state, input: inputSuggestion }); }; - return {state: state.input, setState: setInput, onCancel: onInputCancel, score: scoreInput}; + return {state: state.input, setState: setInput, onCancel: onInputCancel, score: scoreInput, typeInfo: typeInfo.input}; } export function CallBlock(props: CallBlockProps) { @@ -53,7 +54,6 @@ export function CallBlock(props: CallBlockProps) { const addParam = getActions(globalContext, props.setState).c; // const [resolved] = evalExprBlock(props.state, env); // return - const typeInfo = inferTypeCall(props.state, env); return @@ -67,7 +67,7 @@ export function CallBlock(props: CallBlockProps) { {/* { (resolved.kind === "error") && resolved.e.toString() || (resolved.kind === "value") && || "unknown" } */} - :: + :: @@ -92,22 +92,21 @@ function FunctionHeader(props) { } } -function InputParams({ ...rest }) { +function InputParams(props) { const env = useContext(EnvContext); const globalContext = useContext(GlobalContext); - const typeInfo = inferTypeCall(rest.state, env); - const inputEnv = typeInfo.fn.newEnv; - const isOffending = typeInfo.err; + const inputEnv = props.typeInfo.fn.newEnv; + const isOffending = props.typeInfo.err; return
- {rest.state.fn.kind === "call" + {props.state.fn.kind === "call" && globalContext?.syntacticSugar && } {/* Our own input param */}
; diff --git a/src/component/expr/ExprBlock.tsx b/src/component/expr/ExprBlock.tsx index d5beb51..834d06e 100644 --- a/src/component/expr/ExprBlock.tsx +++ b/src/component/expr/ExprBlock.tsx @@ -1,17 +1,16 @@ import { useContext } from "react"; -import { CallBlock, type CallBlockProps, type CallBlockState } from "./CallBlock"; import { EnvContext } from "../../context/EnvContext"; import { GlobalContext } from "../../context/GlobalContext"; +import { type TypeInfo } from "../../eval/infer_type"; +import { getActions } from "../app/actions"; +import { Input } from "../other/Input"; +import { CallBlock, type CallBlockProps, type CallBlockState } from "./CallBlock"; import { InputBlock, type InputBlockProps, type InputBlockState } from "./InputBlock"; import { LambdaBlock, type LambdaBlockProps, type LambdaBlockState } from "./LambdaBlock"; import { LetInBlock, type LetInBlockProps, type LetInBlockState } from "./LetInBlock"; -// import { evalExprBlock, type ResolvedType } from "./eval"; import "./ExprBlock.css"; -import { Input } from "../other/Input"; -import { getActions } from "../app/actions"; -import { inferType, type Type } from "../../eval/infer_type"; export type ExprBlockState = InputBlockState @@ -22,9 +21,15 @@ export type ExprBlockState = export type SetStateFn = (state: InType) => OutType; export interface State2Props { + // Every block gets passed its part of the global app state, and a functino to update that part of the state state: InType; setState: (callback: SetStateFn) => void; + + // To compute the priority of an input suggestion. The root ExprBlock's score function counts the number of errors and subtracts points for every error. Every block passes to its children ExprBlock's a wrapped score-function that creates an app-state with only the child altered, computing a score for that app-state. score: (suggestion: ExprBlockState) => number; + + // All types are inferred once after every App-state change, and passed deeply to all descendants. + typeInfo: TypeInfo; } interface ExprBlockProps extends State2Props { @@ -42,19 +47,15 @@ export function ExprBlock(props: ExprBlockProps) { lambda: () => , }; - // const [resolved] = evalExprBlock(props.state, env); - // const typeInfo = inferType(props.state, env); const actions = getActions(globalContext, props.setState); const extraHandlers = Object.fromEntries(Object.entries(actions).map(([shortcut, action]) => [shortcut, (e) => { e.preventDefault(); action(); }])) - const typeInfo = inferType(props.state, env); - - return + return {renderBlock[props.state.kind]()} - {typeInfo.err && + {props.typeInfo.err &&
- {typeInfo.err.message.split('\n')[1]} + {props.typeInfo.err.message.split('\n')[1]}
} { onCancel: () => void; + typeInfo: TypeInfoInput; } const attemptLiterals = [ @@ -68,11 +70,11 @@ const computeSuggestions = ( })), ]; // return []; // <-- uncomment to disable suggestions (useful for debugging) - return ls.map((state) => [score(state), inferType(state, env).type, state] as PrioritizedSuggestionType) + return ls.map((state) => [score(state), inferTypeInput(state, env).type, state] as PrioritizedSuggestionType) .sort(([a],[b]) => b-a); } -export function InputBlock({ state, setState, score, onCancel }: InputBlockProps) { +export function InputBlock({ state, setState, score, onCancel, typeInfo }: InputBlockProps) { const {text, focus} = state; const globalContext = useContext(GlobalContext); const env = useContext(EnvContext); @@ -133,8 +135,6 @@ export function InputBlock({ state, setState, score, onCancel }: InputBlockProps }, }; - const typeInfo = inferTypeInput(state, env); - return <> extends State2Props { + typeInfo: TypeInfoLambda; } -export function LambdaBlock({state, setState, score}: LambdaBlockProps) { +export function LambdaBlock({state, setState, score, typeInfo}: LambdaBlockProps) { const env = useContext(EnvContext); const setParamName = paramName => setState(state => ({ @@ -37,14 +35,6 @@ export function LambdaBlock({state, setState, score}: LambdaBlockProps) { expr: callback(state.expr), })); - const {paramType, innerEnv} = inferTypeLambda(state, env); - - // const [lambdaResolved, _, innerEnv] = evalLambdaBlock(state.paramName, state.expr, env); - - // const inferredParamType = lambdaResolved.t.params[0](lambdaResolved.t); - - // const innerEnv = env; // todo: change this - return λ   @@ -60,18 +50,19 @@ export function LambdaBlock({state, setState, score}: LambdaBlockProps) { />
-  ::  +  :: 
  :  
- + setState(state => state.expr)} score={suggestion => score({...state, expr: suggestion})} + typeInfo={typeInfo.inner} />
diff --git a/src/component/expr/LetInBlock.tsx b/src/component/expr/LetInBlock.tsx index 1a4ce41..a866f09 100644 --- a/src/component/expr/LetInBlock.tsx +++ b/src/component/expr/LetInBlock.tsx @@ -1,14 +1,13 @@ import { useContext } from "react"; -import { ExprBlock, type ExprBlockState } from "./ExprBlock"; import { EnvContext } from "../../context/EnvContext"; -import { type State2Props } from "./ExprBlock"; import { GlobalContext } from "../../context/GlobalContext"; +import { type TypeInfoLet } from "../../eval/infer_type"; +import { Input } from "../other/Input"; +import { Type } from "../other/Type"; +import { ExprBlock, type ExprBlockState, type State2Props } from "./ExprBlock"; import "./LetInBlock.css"; -import { Input } from "../other/Input"; -import { inferTypeLet } from "../../eval/infer_type"; -import { Type } from "../other/Type"; export interface LetInBlockState { kind: "let"; @@ -19,6 +18,7 @@ export interface LetInBlockState { } export interface LetInBlockProps extends State2Props { + typeInfo: TypeInfoLet; } export function LetInBlock(props: LetInBlockProps) { @@ -32,15 +32,13 @@ export function LetInBlock(props: LetInBlockProps) {
} -function DeclColumns({state, setState, score}) { +function DeclColumns({state, setState, score, typeInfo}) { const env = useContext(EnvContext); const globalContext = useContext(GlobalContext); const setInner = callback => setState(state => ({...state, inner: callback(state.inner)})); const setValue = callback => setState(state => ({...state, value: callback(state.value)})); - const {value: valueTypeInfo, innerEnv} = inferTypeLet(state, env); - return <> let  @@ -53,7 +51,7 @@ function DeclColumns({state, setState, score}) { onTextChange={name => setState(state => ({...state, name}))} extraHandlers={{}} /> - :: + ::  =  @@ -62,45 +60,46 @@ function DeclColumns({state, setState, score}) { setState={setValue} score={suggestion => score({ ...state, value: suggestion })} onCancel={() => setState(state => state.inner)} // keep inner + typeInfo={typeInfo.value} /> {state.inner.kind === "let" && globalContext?.syntacticSugar && - + score({ ...state, inner: suggestion })} + typeInfo={typeInfo.inner} /> } ; } -function InnerMost({state, setState, score}) { +function InnerMost({state, setState, score, typeInfo}) { const env = useContext(EnvContext); const globalContext = useContext(GlobalContext); const setInner = callback => setState(state => ({...state, inner: callback(state.inner)})); - // const [valueResolved] = evalExprBlock(state.value, env); - // const innerEnv = makeInnerEnv(env, state.name, valueResolved); - const {innerEnv} = inferTypeLet(state, env); const onCancel = () => setState(state => state.value); if (state.inner.kind === "let" && globalContext?.syntacticSugar) { - return + return score({ ...state, inner: suggestion })} + typeInfo={typeInfo.inner} /> ; } else { - return + return score({ ...state, inner: suggestion })} onCancel={onCancel} // keep value + typeInfo={typeInfo.inner} /> }