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
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