add deep evaluation + remove environment React context (passed via typeInfo instead) + improve highlighting of statically unknown values
This commit is contained in:
parent
8576f7cb8d
commit
8385f08923
10 changed files with 223 additions and 79 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { extendedEnv } from '../../context/EnvContext';
|
||||
import { extendedEnv } from './environment';
|
||||
import { GlobalContext } from '../../context/GlobalContext';
|
||||
import { inferType, scoreTypeInfo } from '../../eval/infer_type';
|
||||
import { ExprBlock, type ExprBlockState } from '../expr/ExprBlock';
|
||||
|
|
@ -10,6 +10,7 @@ import './App.css';
|
|||
import { evalExpr } from '../../eval/eval';
|
||||
import { Value } from '../other/Value';
|
||||
import { Type, TypeInfoBlock } from '../other/Type';
|
||||
import { deepEvalExpr } from '../../eval/deep_eval';
|
||||
|
||||
|
||||
const examples: [string, ExprBlockState][] = [
|
||||
|
|
@ -141,6 +142,8 @@ export function App() {
|
|||
const typeInfo = useMemo(() => inferType(currentState, extendedEnv), [currentState]);
|
||||
// dynamic evalutions
|
||||
const evalResult = evalExpr(currentState, extendedEnv);
|
||||
const deepEvalResult = deepEvalExpr(currentState, extendedEnv);
|
||||
console.log({deepEvalResult});
|
||||
|
||||
const onCopy = () => {
|
||||
const serialized = JSON.stringify(currentState);
|
||||
|
|
@ -209,6 +212,7 @@ export function App() {
|
|||
return scoreTypeInfo(typeInfo);
|
||||
}}
|
||||
typeInfo={typeInfo}
|
||||
evalResult={deepEvalResult}
|
||||
/>
|
||||
=
|
||||
<Value dynamic={{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import type { ExprBlockState } from "../expr/ExprBlock";
|
||||
|
||||
// Note: new configurations can be added here by using the 'Copy' button in the app.
|
||||
|
||||
export const initialEditorState: ExprBlockState = {
|
||||
kind: "input",
|
||||
text: "",
|
||||
|
|
@ -75,7 +77,7 @@ export const tripleFunctionCallEditorState: ExprBlockState = {
|
|||
},
|
||||
};
|
||||
|
||||
export const biggerExample: ExprBlockState = {"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"input","text":"myList","value":{"kind":"name"}},"name":"myListInc","value":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.map","value":{"kind":"name"},"focus":false},"input":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.map","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"myList","value":{"kind":"name"}}},"input":{"kind":"input","text":"inc","value":{"kind":"name"},"focus":false}}},"input":{"kind":"input","text":"id","value":{"kind":"name"},"focus":true}}},"name":"myList","value":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.push","value":{"kind":"name"},"focus":false},"input":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.push","value":{"kind":"name"},"focus":false},"input":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.push","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"list.emptyList","value":{"kind":"name"},"focus":false}},"input":{"kind":"input","text":"1","value":{"kind":"literal","type":"Int"},"focus":false}}},"input":{"kind":"input","text":"2","value":{"kind":"literal","type":"Int"},"focus":false}}},"input":{"kind":"input","text":"3","value":{"kind":"literal","type":"Int"},"focus":false}}},"name":"id","value":{"kind":"lambda","focus":false,"paramName":"x","expr":{"kind":"input","text":"x","value":{"kind":"name"},"focus":false}}},"name":"inc","value":{"kind":"lambda","focus":false,"paramName":"x","expr":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"addInt","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"x","value":{"kind":"name"},"focus":false}},"input":{"kind":"input","text":"1","value":{"kind":"literal","type":"Int"},"focus":true}}}};
|
||||
export const biggerExample: ExprBlockState = {"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"input","text":"myListInc","value":{"kind":"name"}},"name":"myListInc","value":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.map","value":{"kind":"name"},"focus":false},"input":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.map","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"myList","value":{"kind":"name"}}},"input":{"kind":"input","text":"inc","value":{"kind":"name"},"focus":false}}},"input":{"kind":"input","text":"id","value":{"kind":"name"},"focus":true}}},"name":"myList","value":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.push","value":{"kind":"name"},"focus":false},"input":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.push","value":{"kind":"name"},"focus":false},"input":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.push","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"list.emptyList","value":{"kind":"name"},"focus":false}},"input":{"kind":"input","text":"1","value":{"kind":"literal","type":"Int"},"focus":false}}},"input":{"kind":"input","text":"2","value":{"kind":"literal","type":"Int"},"focus":false}}},"input":{"kind":"input","text":"3","value":{"kind":"literal","type":"Int"},"focus":false}}},"name":"id","value":{"kind":"lambda","focus":false,"paramName":"x","expr":{"kind":"input","text":"x","value":{"kind":"name"}}}},"name":"inc","value":{"kind":"lambda","focus":false,"paramName":"x","expr":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"addInt","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"x","value":{"kind":"name"},"focus":false}},"input":{"kind":"input","text":"1","value":{"kind":"literal","type":"Int"},"focus":true}}}};
|
||||
|
||||
export const lambda2Params: ExprBlockState = {
|
||||
"kind": "let",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { createContext } from "react";
|
||||
import { getDefaultTypeParser, module2Env, ModuleStd } from "dope2";
|
||||
// import type { Dynamic, Environment } from "./eval";
|
||||
import type { StaticEnvironment } from "../eval/infer_type";
|
||||
import type { StaticEnvironment } from "../../eval/infer_type";
|
||||
|
||||
export const functionWith3Params = i => j => k => i+j+k;
|
||||
export const functionWith4Params = i => j => k => l => i+j+k+l;
|
||||
|
|
@ -20,5 +19,3 @@ export const extendedEnv: StaticEnvironment = {
|
|||
// nextFreeTypeVar: 0,
|
||||
typevars: new Set(),
|
||||
};
|
||||
|
||||
export const EnvContext = createContext(extendedEnv);
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
import { useContext } from "react";
|
||||
|
||||
import { CallContext } from "../../context/CallContext";
|
||||
import { EnvContext } from "../../context/EnvContext";
|
||||
import { GlobalContext } from "../../context/GlobalContext";
|
||||
import { type StaticEnvironment, type TypeInfoCall } from "../../eval/infer_type";
|
||||
import { type TypeInfoCall } from "../../eval/infer_type";
|
||||
import { getActions } from "../app/actions";
|
||||
import { Type, TypeInfoBlock } from "../other/Type";
|
||||
import { TypeInfoBlock } from "../other/Type";
|
||||
import "./CallBlock.css";
|
||||
import { ExprBlock, type ExprBlockState, type SetStateFn, type State2Props } from "./ExprBlock";
|
||||
import type { DeepEvalResultCall } from "../../eval/deep_eval";
|
||||
|
||||
export interface CallBlockState {
|
||||
kind: "call";
|
||||
|
|
@ -20,9 +20,10 @@ export interface CallBlockProps<
|
|||
InputState=ExprBlockState,
|
||||
> extends State2Props<CallBlockState,ExprBlockState> {
|
||||
typeInfo: TypeInfoCall;
|
||||
evalResult: DeepEvalResultCall;
|
||||
}
|
||||
|
||||
function nestedFnProperties({state, setState, score, typeInfo}: CallBlockProps, env: StaticEnvironment) {
|
||||
function nestedFnProperties({state, setState, score, typeInfo, evalResult}: CallBlockProps) {
|
||||
const setFn = (callback: SetStateFn) => {
|
||||
setState(state => ({...state, fn: callback(state.fn)}));
|
||||
};
|
||||
|
|
@ -32,10 +33,10 @@ function nestedFnProperties({state, setState, score, typeInfo}: CallBlockProps,
|
|||
const scoreFn = (fnSuggestion: ExprBlockState) => {
|
||||
return score({ ...state, fn: fnSuggestion });
|
||||
};
|
||||
return {state: state.fn, setState: setFn, onCancel: onFnCancel, score: scoreFn, typeInfo: typeInfo.fn};
|
||||
return {state: state.fn, setState: setFn, onCancel: onFnCancel, score: scoreFn, typeInfo: typeInfo.fn, evalResult: evalResult.fn};
|
||||
}
|
||||
|
||||
function nestedInputProperties({state, setState, score, typeInfo}: CallBlockProps, env: StaticEnvironment) {
|
||||
function nestedInputProperties({state, setState, score, typeInfo, evalResult}: CallBlockProps) {
|
||||
const setInput = (callback: SetStateFn) => {
|
||||
setState(state => ({...state, input: callback(state.input)}));
|
||||
};
|
||||
|
|
@ -45,7 +46,7 @@ function nestedInputProperties({state, setState, score, typeInfo}: CallBlockProp
|
|||
const scoreInput = (inputSuggestion: ExprBlockState) => {
|
||||
return score({ ...state, input: inputSuggestion });
|
||||
};
|
||||
return {state: state.input, setState: setInput, onCancel: onInputCancel, score: scoreInput, typeInfo: typeInfo.input};
|
||||
return {state: state.input, setState: setInput, onCancel: onInputCancel, score: scoreInput, typeInfo: typeInfo.input, evalResult: evalResult.input};
|
||||
}
|
||||
|
||||
export function CallBlock(props: CallBlockProps) {
|
||||
|
|
@ -69,9 +70,8 @@ export function CallBlock(props: CallBlockProps) {
|
|||
}
|
||||
|
||||
function FunctionHeader(props) {
|
||||
const env = useContext(EnvContext);
|
||||
const globalContext = useContext(GlobalContext);
|
||||
const nestedProperties = nestedFnProperties(props, env);
|
||||
const nestedProperties = nestedFnProperties(props);
|
||||
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
|
||||
|
|
@ -87,21 +87,17 @@ function FunctionHeader(props) {
|
|||
}
|
||||
|
||||
function InputParams(props) {
|
||||
const env = useContext(EnvContext);
|
||||
const globalContext = useContext(GlobalContext);
|
||||
const inputEnv = props.typeInfo.fn.newEnv;
|
||||
const isOffending = props.typeInfo.err;
|
||||
return <div className={"inputParam" + (isOffending ? " offending" : "")}>
|
||||
{props.state.fn.kind === "call"
|
||||
&& globalContext?.syntacticSugar
|
||||
&& <InputParams
|
||||
{...nestedFnProperties(props as CallBlockProps, env)}
|
||||
{...nestedFnProperties(props as CallBlockProps)}
|
||||
/>}
|
||||
{/* Our own input param */}
|
||||
<EnvContext value={inputEnv}>
|
||||
<ExprBlock
|
||||
{...nestedInputProperties(props as CallBlockProps, env)}
|
||||
/>
|
||||
</EnvContext>
|
||||
<ExprBlock
|
||||
{...nestedInputProperties(props as CallBlockProps)}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { useContext } from "react";
|
||||
|
||||
import { EnvContext } from "../../context/EnvContext";
|
||||
import { GlobalContext } from "../../context/GlobalContext";
|
||||
import { type TypeInfo } from "../../eval/infer_type";
|
||||
import { getActions } from "../app/actions";
|
||||
|
|
@ -11,7 +10,7 @@ import { LambdaBlock, type LambdaBlockProps, type LambdaBlockState } from "./Lam
|
|||
import { LetInBlock, type LetInBlockProps, type LetInBlockState } from "./LetInBlock";
|
||||
|
||||
import "./ExprBlock.css";
|
||||
import { evalExpr } from "../../eval/eval";
|
||||
import type { DeepEvalResult } from "../../eval/deep_eval";
|
||||
|
||||
export type ExprBlockState =
|
||||
InputBlockState
|
||||
|
|
@ -31,6 +30,8 @@ export interface State2Props<InType, OutType = InType> {
|
|||
|
||||
// All types are inferred once after every App-state change, and passed deeply to all descendants.
|
||||
typeInfo: TypeInfo;
|
||||
|
||||
evalResult: DeepEvalResult;
|
||||
}
|
||||
|
||||
interface ExprBlockProps extends State2Props<ExprBlockState> {
|
||||
|
|
@ -38,7 +39,6 @@ interface ExprBlockProps extends State2Props<ExprBlockState> {
|
|||
}
|
||||
|
||||
export function ExprBlock(props: ExprBlockProps) {
|
||||
const env = useContext(EnvContext);
|
||||
const globalContext = useContext(GlobalContext);
|
||||
|
||||
const renderBlock = {
|
||||
|
|
@ -51,14 +51,9 @@ export function ExprBlock(props: ExprBlockProps) {
|
|||
const actions = getActions(globalContext, props.setState);
|
||||
const extraHandlers = Object.fromEntries(Object.entries(actions).map(([shortcut, action]) =>
|
||||
[shortcut, (e) => { e.preventDefault(); action(); }]));
|
||||
const evalResult = evalExpr(props.state, env);
|
||||
const err = props.typeInfo.err || evalResult.err;
|
||||
return <span className={"editor" + (err ? " error" : "") + ((evalResult.val === undefined) ? " unknown" : "")}>
|
||||
const err = props.typeInfo.err || props.evalResult.err;
|
||||
return <span className={"editor" + (err ? " error" : "") + ((props.evalResult.val === undefined) ? " unknown" : "")}>
|
||||
{renderBlock[props.state.kind]()}
|
||||
{/* {(err !== undefined) &&
|
||||
(<div className="errorMessage">
|
||||
{err.message.trim()}
|
||||
</div>)} */}
|
||||
<Input
|
||||
placeholder="<c>"
|
||||
text=""
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { memo, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|||
import { trie } from "dope2";
|
||||
|
||||
import { CallContext } from "../../context/CallContext";
|
||||
import { EnvContext } from "../../context/EnvContext";
|
||||
import { GlobalContext } from "../../context/GlobalContext";
|
||||
import { inferTypeInput, type StaticEnvironment, type Type, type TypeInfoInput } from "../../eval/infer_type";
|
||||
import { getActions } from "../app/actions";
|
||||
|
|
@ -88,7 +87,7 @@ const computeSuggestions = (
|
|||
export function InputBlock({ state, setState, score, onCancel, typeInfo }: InputBlockProps) {
|
||||
const {text, focus} = state;
|
||||
const globalContext = useContext(GlobalContext);
|
||||
const env = useContext(EnvContext);
|
||||
const env = typeInfo.env;
|
||||
const callContext = useContext(CallContext);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [i, setI] = useState(0); // selected suggestion idx
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
import { useContext } from "react";
|
||||
|
||||
import { EnvContext } from "../../context/EnvContext";
|
||||
import { ExprBlock, type ExprBlockState, type State2Props } from "./ExprBlock";
|
||||
|
||||
import { type TypeInfoLambda } from "../../eval/infer_type";
|
||||
import { Input } from "../other/Input";
|
||||
import { Type } from "../other/Type";
|
||||
import "./LambdaBlock.css";
|
||||
import type { DeepEvalResultLambda } from "../../eval/deep_eval";
|
||||
|
||||
export interface LambdaBlockState {
|
||||
kind: "lambda";
|
||||
|
|
@ -20,12 +18,10 @@ export interface LambdaBlockProps<
|
|||
InputState=ExprBlockState,
|
||||
> extends State2Props<LambdaBlockState,ExprBlockState> {
|
||||
typeInfo: TypeInfoLambda;
|
||||
evalResult: DeepEvalResultLambda;
|
||||
}
|
||||
|
||||
|
||||
export function LambdaBlock({state, setState, score, typeInfo}: LambdaBlockProps) {
|
||||
const env = useContext(EnvContext);
|
||||
|
||||
export function LambdaBlock({state, setState, score, typeInfo, evalResult}: LambdaBlockProps) {
|
||||
const setParamName = paramName => setState(state => ({
|
||||
...state,
|
||||
paramName,
|
||||
|
|
@ -56,15 +52,14 @@ export function LambdaBlock({state, setState, score, typeInfo}: LambdaBlockProps
|
|||
<span className="keyword">:</span>
|
||||
|
||||
<div className="lambdaInner">
|
||||
<EnvContext value={typeInfo.innerEnv}>
|
||||
<ExprBlock
|
||||
state={state.expr}
|
||||
setState={setExpr}
|
||||
onCancel={() => setState(state => state.expr)}
|
||||
score={suggestion => score({...state, expr: suggestion})}
|
||||
typeInfo={typeInfo.inner}
|
||||
/>
|
||||
</EnvContext>
|
||||
<ExprBlock
|
||||
state={state.expr}
|
||||
setState={setExpr}
|
||||
onCancel={() => setState(state => state.expr)}
|
||||
score={suggestion => score({...state, expr: suggestion})}
|
||||
typeInfo={typeInfo.inner}
|
||||
evalResult={evalResult.inner}
|
||||
/>
|
||||
</div>
|
||||
</span>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { useContext } from "react";
|
||||
|
||||
import { EnvContext } from "../../context/EnvContext";
|
||||
import { GlobalContext } from "../../context/GlobalContext";
|
||||
import { type TypeInfoLet } from "../../eval/infer_type";
|
||||
import { Input } from "../other/Input";
|
||||
|
|
@ -32,7 +31,7 @@ export function LetInBlock(props: LetInBlockProps) {
|
|||
</span>
|
||||
}
|
||||
|
||||
function DeclColumns({state, setState, score, typeInfo}) {
|
||||
function DeclColumns({state, setState, score, typeInfo, evalResult}) {
|
||||
const globalContext = useContext(GlobalContext);
|
||||
|
||||
const setInner = callback => setState(state => ({...state, inner: callback(state.inner)}));
|
||||
|
|
@ -61,45 +60,43 @@ function DeclColumns({state, setState, score, typeInfo}) {
|
|||
score={suggestion => score({ ...state, value: suggestion })}
|
||||
onCancel={() => setState(state => state.inner)} // keep inner
|
||||
typeInfo={typeInfo.value}
|
||||
evalResult={evalResult.value}
|
||||
/>
|
||||
</span>
|
||||
{state.inner.kind === "let" &&
|
||||
globalContext?.syntacticSugar &&
|
||||
<EnvContext value={typeInfo.innerEnv}>
|
||||
<DeclColumns
|
||||
state={state.inner}
|
||||
setState={setInner}
|
||||
score={suggestion => score({ ...state, inner: suggestion })}
|
||||
typeInfo={typeInfo.inner}
|
||||
/>
|
||||
</EnvContext>
|
||||
<DeclColumns
|
||||
state={state.inner}
|
||||
setState={setInner}
|
||||
score={suggestion => score({ ...state, inner: suggestion })}
|
||||
typeInfo={typeInfo.inner}
|
||||
evalResult={evalResult.inner}
|
||||
/>
|
||||
}
|
||||
</>;
|
||||
}
|
||||
|
||||
function InnerMost({state, setState, score, typeInfo}) {
|
||||
function InnerMost({state, setState, score, typeInfo, evalResult}) {
|
||||
const globalContext = useContext(GlobalContext);
|
||||
const setInner = callback => setState(state => ({...state, inner: callback(state.inner)}));
|
||||
const onCancel = () => setState(state => state.value);
|
||||
if (state.inner.kind === "let" && globalContext?.syntacticSugar) {
|
||||
return <EnvContext value={typeInfo.innerEnv}>
|
||||
<InnerMost
|
||||
state={state.inner}
|
||||
setState={setInner}
|
||||
score={suggestion => score({ ...state, inner: suggestion })}
|
||||
typeInfo={typeInfo.inner}
|
||||
/>
|
||||
</EnvContext>;
|
||||
return <InnerMost
|
||||
state={state.inner}
|
||||
setState={setInner}
|
||||
score={suggestion => score({ ...state, inner: suggestion })}
|
||||
typeInfo={typeInfo.inner}
|
||||
evalResult={evalResult.inner}
|
||||
/>;
|
||||
}
|
||||
else {
|
||||
return <EnvContext value={typeInfo.innerEnv}>
|
||||
<ExprBlock
|
||||
state={state.inner}
|
||||
setState={setInner}
|
||||
score={suggestion => score({ ...state, inner: suggestion })}
|
||||
onCancel={onCancel} // keep value
|
||||
typeInfo={typeInfo.inner}
|
||||
/>
|
||||
</EnvContext>
|
||||
return <ExprBlock
|
||||
state={state.inner}
|
||||
setState={setInner}
|
||||
score={suggestion => score({ ...state, inner: suggestion })}
|
||||
onCancel={onCancel} // keep value
|
||||
typeInfo={typeInfo.inner}
|
||||
evalResult={evalResult.inner}
|
||||
/>;
|
||||
}
|
||||
}
|
||||
151
src/eval/deep_eval.ts
Normal file
151
src/eval/deep_eval.ts
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
import { trie } from "dope2";
|
||||
import type { CallBlockState } from "../component/expr/CallBlock";
|
||||
import type { ExprBlockState } from "../component/expr/ExprBlock";
|
||||
import type { InputBlockState } from "../component/expr/InputBlock";
|
||||
import type { LambdaBlockState } from "../component/expr/LambdaBlock";
|
||||
import type { LetInBlockState } from "../component/expr/LetInBlock";
|
||||
import { evalExpr, type DynamicEnvironment, type EvalResult } from "./eval";
|
||||
|
||||
export interface DeepEvalResultInput extends EvalResult {
|
||||
kind: "input";
|
||||
}
|
||||
export interface DeepEvalResultCall extends EvalResult {
|
||||
kind: "call";
|
||||
fn: DeepEvalResult;
|
||||
input: DeepEvalResult;
|
||||
}
|
||||
export interface DeepEvalResultLet extends EvalResult {
|
||||
kind: "let";
|
||||
value: DeepEvalResult;
|
||||
inner: DeepEvalResult;
|
||||
}
|
||||
export interface DeepEvalResultLambda extends EvalResult {
|
||||
kind: "lambda";
|
||||
inner: DeepEvalResult;
|
||||
}
|
||||
export type DeepEvalResult = DeepEvalResultInput | DeepEvalResultCall | DeepEvalResultLet | DeepEvalResultLambda;
|
||||
|
||||
export function deepEvalExpr(s: ExprBlockState, env: DynamicEnvironment): DeepEvalResult {
|
||||
if (s.kind === "input") {
|
||||
return deepEvalInput(s, env);
|
||||
}
|
||||
else if (s.kind === "call") {
|
||||
return deepEvalCall(s, env);
|
||||
}
|
||||
else if (s.kind === "let") {
|
||||
return deepEvalLet(s, env);
|
||||
}
|
||||
else { // (s.kind === "lambda")
|
||||
return deepEvalLambda(s, env);
|
||||
}
|
||||
}
|
||||
|
||||
export function deepEvalInput(s: InputBlockState, env: DynamicEnvironment): DeepEvalResultInput {
|
||||
if (s.value.kind === "literal") {
|
||||
if (s.text === '') {
|
||||
return {
|
||||
kind: "input",
|
||||
err: new Error('cannot parse empty string as '+s.value.type),
|
||||
};
|
||||
}
|
||||
const ctor = {
|
||||
Int: BigInt,
|
||||
Double: Number,
|
||||
}[s.value.type] as (s: string) => any;
|
||||
return {
|
||||
kind: "input",
|
||||
val: ctor(s.text)
|
||||
};
|
||||
}
|
||||
else if (s.value.kind === "name") {
|
||||
const found = trie.get(env.names)(s.text);
|
||||
if (found) {
|
||||
if (found.recursive) {
|
||||
// dirty
|
||||
return {
|
||||
kind: "input",
|
||||
...found.i(),
|
||||
};
|
||||
}
|
||||
return {
|
||||
kind: "input",
|
||||
val: found.i,
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
kind: "input",
|
||||
err: new Error(`'${s.text}' not found`),
|
||||
}
|
||||
}
|
||||
export function deepEvalCall(s: CallBlockState, env: DynamicEnvironment): DeepEvalResultCall {
|
||||
const fn = deepEvalExpr(s.fn, env);
|
||||
const input = deepEvalExpr(s.input, env);
|
||||
if (fn.val !== undefined && input.val !== undefined) {
|
||||
try {
|
||||
const result = fn.val(input.val)
|
||||
return {
|
||||
kind: "call",
|
||||
val: result,
|
||||
fn,
|
||||
input,
|
||||
};
|
||||
}
|
||||
catch (e: any) {
|
||||
return {
|
||||
kind: "call",
|
||||
err: e,
|
||||
fn,
|
||||
input,
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
kind: "call",
|
||||
fn,
|
||||
input,
|
||||
}
|
||||
}
|
||||
export function deepEvalLet(s: LetInBlockState, env: DynamicEnvironment): DeepEvalResultLet {
|
||||
const valueEnv = {
|
||||
names: trie.insert(env.names)(s.name)({
|
||||
recursive: true,
|
||||
i: () => {
|
||||
try {
|
||||
return { val: valueResult.val };
|
||||
} catch (e) {
|
||||
return { err: e };
|
||||
}
|
||||
},
|
||||
}),
|
||||
};
|
||||
const valueResult = deepEvalExpr(s.value, valueEnv);
|
||||
const innerEnv = {
|
||||
names: trie.insert(env.names)(s.name)({i: valueResult.val}),
|
||||
}
|
||||
const innerResult = deepEvalExpr(s.inner, innerEnv)
|
||||
return {
|
||||
kind: "let",
|
||||
val: innerResult.val,
|
||||
err: innerResult.err,
|
||||
inner: innerResult,
|
||||
value: valueResult,
|
||||
};
|
||||
}
|
||||
export function deepEvalLambda(s: LambdaBlockState, env: DynamicEnvironment): DeepEvalResultLambda {
|
||||
const fn = x => {
|
||||
const innerEnv = {
|
||||
names: trie.insert(env.names)(s.paramName)({i: x})
|
||||
};
|
||||
const result = evalExpr(s.expr, innerEnv); // shallow eval
|
||||
return result.val;
|
||||
};
|
||||
const staticResult = deepEvalExpr(s.expr, {
|
||||
names: trie.insert(env.names)(s.paramName)({i: undefined}),
|
||||
});
|
||||
return {
|
||||
kind: "lambda",
|
||||
val: fn,
|
||||
inner: staticResult,
|
||||
};
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ export type Substitutions = Map<string, Type>;
|
|||
interface TypeInfoCommon {
|
||||
type: Type;
|
||||
subs: Substitutions;
|
||||
env: StaticEnvironment;
|
||||
newEnv: StaticEnvironment;
|
||||
err?: IncompatibleTypesError;
|
||||
}
|
||||
|
|
@ -74,6 +75,7 @@ export const inferTypeInput = memoize(function inferTypeInput(s: InputBlockState
|
|||
kind: "input",
|
||||
type,
|
||||
subs: new Map(),
|
||||
env,
|
||||
newEnv: env,
|
||||
}
|
||||
}
|
||||
|
|
@ -87,6 +89,7 @@ export const inferTypeInput = memoize(function inferTypeInput(s: InputBlockState
|
|||
kind: "input",
|
||||
type,
|
||||
subs: new Map(),
|
||||
env,
|
||||
newEnv,
|
||||
};
|
||||
}
|
||||
|
|
@ -97,6 +100,7 @@ export const inferTypeInput = memoize(function inferTypeInput(s: InputBlockState
|
|||
kind: "input",
|
||||
type,
|
||||
subs: new Map(),
|
||||
env,
|
||||
newEnv,
|
||||
err: new Error(`'${s.text}' not found`),
|
||||
}
|
||||
|
|
@ -140,6 +144,7 @@ export const inferTypeCall = memoize(function inferTypeCall(s: CallBlockState, e
|
|||
kind: "call",
|
||||
type,
|
||||
subs: mergedSubs,
|
||||
env,
|
||||
newEnv,
|
||||
fn: fnTypeInfo,
|
||||
input: inputTypeInfo,
|
||||
|
|
@ -152,6 +157,7 @@ export const inferTypeCall = memoize(function inferTypeCall(s: CallBlockState, e
|
|||
kind: "call",
|
||||
type,
|
||||
subs: new Map(),
|
||||
env,
|
||||
newEnv,
|
||||
err: e,
|
||||
fn: fnTypeInfo,
|
||||
|
|
@ -177,6 +183,7 @@ export const inferTypeLet = memoize(function inferTypeLet(s: LetInBlockState, en
|
|||
type: innerTypeInfo.type,
|
||||
value: recursiveTypeInfo.inner,
|
||||
subs: innerTypeInfo.subs,
|
||||
env,
|
||||
newEnv: innerTypeInfo.newEnv,
|
||||
inner: innerTypeInfo,
|
||||
innerEnv,
|
||||
|
|
@ -189,6 +196,7 @@ export const inferTypeLambda = memoize(function inferTypeLambda(s: LambdaBlockSt
|
|||
kind: "lambda",
|
||||
type: fnType(_ => recursiveTypeInfo.paramType)(_ => recursiveTypeInfo.inner.type),
|
||||
...recursiveTypeInfo,
|
||||
env,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue