import { Expr, FieldName, ProductPattern, Tag, VariableName } from "../expr"; import { ThrownRuntimeError } from "./error"; export type Value = | { tag: "string", value: string } | { tag: "number", value: number } | { tag: "tag", tag_name: Tag } | { tag: "tagged", tag_name: Tag, value: Value } | { tag: "tuple", values: Value[] } | { tag: "record", fields: Map } | { tag: "closure", closure: Closure } export type ValueTag = | "string" | "number" | "tag" | "tagged" | "tuple" | "record" | "closure" // Used as a Stack of frames. Basically a linked list. export type Env = | { tag: "nil" } | { tag: "frame", frame: EnvFrame, parent: Env } export type EnvFrame = Map; export type Closure = { env: Env, parameters: ProductPattern[], body: Expr, } export namespace Value { export const string = (value: string): Value => ({ tag: "string", value }); export const number = (value: number): Value => ({ tag: "number", value }); export const tag = (tag_name: Tag): Value => ({ tag: "tag", tag_name }); export const tagged = (tag_name: Tag, value: Value): Value => ({ tag: "tagged", tag_name, value }); export const tuple = (values: Value[]): Value => ({ tag: "tuple", values }); export const record = (fields: Map): Value => ({ tag: "record", fields }); export const closure = (closure: Closure): Value => ({ tag: "closure", closure }); } export namespace Env { export function nil(): Env { return { tag: "nil" }; } export function push_frame(env: Env, frame: EnvFrame): Env { return { tag: "frame", frame, parent: env }; } // may throw `ThrownRuntimeError` export function lookup(env: Env, var_name: VariableName): Value { let cur = env; while (cur.tag !== "nil") { if (cur.frame.has(var_name)) { return cur.frame.get(var_name)!; } cur = cur.parent; } throw ThrownRuntimeError.error({ tag: "VariableLookupFailure", name: var_name, }); } export function frame_insert_mut(frame: EnvFrame, var_name: VariableName, value: Value) { frame.set(var_name, value); } }