greatly simplify state + cleanup code

This commit is contained in:
Joeri Exelmans 2025-05-14 08:09:35 +02:00
parent 2d0deca127
commit 5964510036
11 changed files with 268 additions and 321 deletions

92
src/eval.ts Normal file
View file

@ -0,0 +1,92 @@
import { apply, Double, Int, NotAFunctionError, trie, UnifyError } from "dope2";
import type { EditorState } from "./Editor";
import type { InputValueType } from "./InputBlock";
import { makeInnerEnv } from "./LetInBlock";
import { parseDouble, parseInt } from "./util/parse";
export class DeepError {
e: Error;
depth: number;
constructor(e, depth) {
this.e = e;
this.depth = depth;
}
};
// a dynamically typed value = tuple (instance, type)
export interface Dynamic {
i: any;
t: any;
};
// the value of every block is either known (Dynamic), an error, or unknown
export type ResolvedType = Dynamic | DeepError | undefined;
export const evalEditorBlock = (s: EditorState, env): ResolvedType => {
if (s.kind === "input") {
return evalInputBlock(s.text, s.value, env);
}
if (s.kind === "call") {
const fn = evalEditorBlock(s.fn, env);
const input = evalEditorBlock(s.input, env);
return evalCallBlock(fn, input);
}
if (s.kind === "let") {
const value = evalEditorBlock(s.value, env);
const innerEnv = makeInnerEnv(env, s.name, value)
return evalEditorBlock(s.inner, innerEnv);
}
if (s.kind === "lambda") {
const expr = evalEditorBlock(s.expr, env);
return undefined; // todo
}
};
export function evalInputBlock(text: string, value: InputValueType, env): ResolvedType {
if (value.kind === "literal") {
return parseLiteral(text, value.type);
}
else if (value.kind === "name") {
return trie.get(env.name2dyn)(text);
}
else { // kind === "text" -> unresolved
return;
}
}
export function evalCallBlock(fn: ResolvedType, input: ResolvedType) {
if (haveValue(input) && haveValue(fn)) {
try {
const outputResolved = apply(input)(fn); // may throw
return outputResolved; // success
}
catch (e) {
if (!(e instanceof UnifyError) && !(e instanceof NotAFunctionError)) {
throw e;
}
return new DeepError(e, 0); // eval error
}
}
else if (input instanceof DeepError) {
return input; // bubble up the error
}
else if (fn instanceof DeepError) {
return new DeepError(fn.e, fn.depth+1);
}
}
function parseLiteral(text: string, type: string) {
// dirty
if (type === "Int") {
return { i: parseInt(text), t: Int };
}
if (type === "Double") {
return { i: parseDouble(text), t: Double };
}
}
export function haveValue(resolved: ResolvedType) {
return resolved && !(resolved instanceof DeepError);
}