dope2-webapp/src/eval/eval.ts

106 lines
2.6 KiB
TypeScript

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";
export interface DynamicEnvironment {
names: any;
}
export interface EvalResult {
val?: any;
err?: Error;
};
export function evalExpr(s: ExprBlockState, env: DynamicEnvironment): EvalResult {
if (s.kind === "input") {
return evalInput(s, env);
}
else if (s.kind === "call") {
return evalCall(s, env);
}
else if (s.kind === "let") {
return evalLet(s, env);
}
else { // (s.kind === "lambda")
return evalLambda(s, env);
}
}
export function evalInput(s: InputBlockState, env: DynamicEnvironment): EvalResult {
if (s.value.kind === "literal") {
if (s.text === '') {
return {
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 {
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 found.i();
}
return {val: found.i};
}
}
return {
err: new Error(`'${s.text}' not found`),
}
}
export function evalCall(s: CallBlockState, env: DynamicEnvironment): EvalResult {
const fn = evalExpr(s.fn, env);
const input = evalExpr(s.input, env);
if (fn.val !== undefined && input.val !== undefined) {
try {
const result = fn.val(input.val)
return { val: result } ;
}
catch (e: any) {
return { err: e };
}
}
return {};
}
export function evalLet(s: LetInBlockState, env: DynamicEnvironment): EvalResult {
const valueEnv = {
names: trie.insert(env.names)(s.name)({
recursive: true,
i: () => {
try {
return { val };
} catch (e) {
return { err: e };
}
},
}),
};
const {val} = evalExpr(s.value, valueEnv);
const innerEnv = {
names: trie.insert(env.names)(s.name)({i: val}),
}
return evalExpr(s.inner, innerEnv);
}
export function evalLambda(s: LambdaBlockState, env: DynamicEnvironment): EvalResult {
const fn = x => {
const innerEnv = {
names: trie.insert(env.names)(s.paramName)({i: x})
};
const result = evalExpr(s.expr, innerEnv);
return result.val;
};
return {val: fn};
}