display dynamic errors too

This commit is contained in:
Joeri Exelmans 2025-05-28 08:13:33 +02:00
parent abd3bd3645
commit 955bb17f9a
3 changed files with 46 additions and 17 deletions

View file

@ -137,8 +137,10 @@ export function App() {
const currentState = appState.history.at(-1)!; const currentState = appState.history.at(-1)!;
// infer types for entire app state // static evalution
const typeInfo = useMemo(() => inferType(currentState, extendedEnv), [currentState]); const typeInfo = useMemo(() => inferType(currentState, extendedEnv), [currentState]);
// dynamic evalutions
const evalResult = evalExpr(currentState, extendedEnv);
return ( return (
<> <>
@ -186,7 +188,7 @@ export function App() {
/> />
= =
<Value dynamic={{ <Value dynamic={{
i: evalExpr(currentState, extendedEnv), i: evalResult.val,
t: typeInfo.type, t: typeInfo.type,
}}/> }}/>
:: ::

View file

@ -11,6 +11,7 @@ import { LambdaBlock, type LambdaBlockProps, type LambdaBlockState } from "./Lam
import { LetInBlock, type LetInBlockProps, type LetInBlockState } from "./LetInBlock"; import { LetInBlock, type LetInBlockProps, type LetInBlockState } from "./LetInBlock";
import "./ExprBlock.css"; import "./ExprBlock.css";
import { evalExpr } from "../../eval/eval";
export type ExprBlockState = export type ExprBlockState =
InputBlockState InputBlockState
@ -49,12 +50,13 @@ export function ExprBlock(props: ExprBlockProps) {
const actions = getActions(globalContext, props.setState); const actions = getActions(globalContext, props.setState);
const extraHandlers = Object.fromEntries(Object.entries(actions).map(([shortcut, action]) => const extraHandlers = Object.fromEntries(Object.entries(actions).map(([shortcut, action]) =>
[shortcut, (e) => { e.preventDefault(); action(); }])) [shortcut, (e) => { e.preventDefault(); action(); }]));
return <span className={"editor" + (props.typeInfo.err ? " error" : "")}> const err = props.typeInfo.err || evalExpr(props.state, env).err;
return <span className={"editor" + (err ? " error" : "")}>
{renderBlock[props.state.kind]()} {renderBlock[props.state.kind]()}
{(props.typeInfo.err !== undefined) && {(err !== undefined) &&
(<div className="errorMessage"> (<div className="errorMessage">
{props.typeInfo.err.message.trim()} {err.message.trim()}
</div>)} </div>)}
<Input <Input
placeholder="<c>" placeholder="<c>"

View file

@ -9,7 +9,10 @@ export interface DynamicEnvironment {
names: any; names: any;
} }
export type EvalResult = any; export interface EvalResult {
val?: any;
err?: Error;
};
export function evalExpr(s: ExprBlockState, env: DynamicEnvironment): EvalResult { export function evalExpr(s: ExprBlockState, env: DynamicEnvironment): EvalResult {
if (s.kind === "input") { if (s.kind === "input") {
@ -28,44 +31,65 @@ export function evalExpr(s: ExprBlockState, env: DynamicEnvironment): EvalResult
export function evalInput(s: InputBlockState, env: DynamicEnvironment): EvalResult { export function evalInput(s: InputBlockState, env: DynamicEnvironment): EvalResult {
if (s.value.kind === "literal") { if (s.value.kind === "literal") {
if (s.text === '') {
return {
err: new Error('cannot parse empty string as '+s.value.type)
};
}
const ctor = { const ctor = {
Int: BigInt, Int: BigInt,
Double: Number, Double: Number,
}[s.value.type] as (s: string) => any; }[s.value.type] as (s: string) => any;
return ctor(s.text); return {
val: ctor(s.text)
};
} }
else if (s.value.kind === "name") { else if (s.value.kind === "name") {
const found = trie.get(env.names)(s.text); const found = trie.get(env.names)(s.text);
if (found) { if (found) {
if (found.recursive) { if (found.recursive) {
// dirty
return found.i(); return found.i();
} }
return found.i; return {val: found.i};
} }
} }
return {
err: new Error(`'${s.text}' not found`),
}
} }
export function evalCall(s: CallBlockState, env: DynamicEnvironment): EvalResult { export function evalCall(s: CallBlockState, env: DynamicEnvironment): EvalResult {
const fn = evalExpr(s.fn, env); const fn = evalExpr(s.fn, env);
const input = evalExpr(s.input, env); const input = evalExpr(s.input, env);
if (fn !== undefined && input !== undefined) { if (fn.val !== undefined && input.val !== undefined) {
try { try {
return fn(input); const result = fn.val(input.val)
return { val: result } ;
} }
catch {} catch (e: any) {
return { err: e };
} }
}
return {};
} }
export function evalLet(s: LetInBlockState, env: DynamicEnvironment): EvalResult { export function evalLet(s: LetInBlockState, env: DynamicEnvironment): EvalResult {
const valueEnv = { const valueEnv = {
names: trie.insert(env.names)(s.name)({ names: trie.insert(env.names)(s.name)({
recursive: true, recursive: true,
i: () => { try { return value; } catch (e) {} }, i: () => {
try {
return { val };
} catch (e) {
return { err: e };
}
},
}), }),
}; };
const value = evalExpr(s.value, valueEnv); const {val} = evalExpr(s.value, valueEnv);
const innerEnv = { const innerEnv = {
names: trie.insert(env.names)(s.name)({i: value}), names: trie.insert(env.names)(s.name)({i: val}),
} }
return evalExpr(s.inner, innerEnv); return evalExpr(s.inner, innerEnv);
} }
@ -75,7 +99,8 @@ export function evalLambda(s: LambdaBlockState, env: DynamicEnvironment): EvalRe
const innerEnv = { const innerEnv = {
names: trie.insert(env.names)(s.paramName)({i: x}) names: trie.insert(env.names)(s.paramName)({i: x})
}; };
return evalExpr(s.expr, innerEnv); const result = evalExpr(s.expr, innerEnv);
return result.val;
}; };
return fn; return {val: fn};
} }