add deep evaluation + remove environment React context (passed via typeInfo instead) + improve highlighting of statically unknown values

This commit is contained in:
Joeri Exelmans 2025-05-28 12:20:31 +02:00
parent 8576f7cb8d
commit 8385f08923
10 changed files with 223 additions and 79 deletions

151
src/eval/deep_eval.ts Normal file
View 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,
};
}

View file

@ -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,
};
});