hivecast-sdk/packages/omega-core/src/membranePrimitives.ts

354 lines
13 KiB
TypeScript
Raw Normal View History

import type { Env } from "../../../src/core/eval/env";
import { envEmpty, envSet } from "../../../src/core/eval/env";
import type { Store } from "../../../src/core/eval/store";
import type { State, Frame, StepOutcome } from "../../../src/core/eval/machine";
import type { Val } from "../../../src/core/eval/values";
import { VUnit, VTrue, VFalse } from "../../../src/core/eval/values";
import { registerConditionPrims } from "../../../src/core/conditions/prims";
function deepEqual(a: Val, b: Val): boolean {
if (a.tag !== b.tag) return false;
switch (a.tag) {
case "Unit":
return true;
case "Num":
return (b as { n: number }).n === a.n;
case "Bool":
return (b as { b: boolean }).b === a.b;
case "Str":
return (b as { s: string }).s === a.s;
case "Sym":
return (b as { name: string }).name === a.name;
case "Vector": {
const other = b as { items: Val[] };
if (other.items.length !== a.items.length) return false;
for (let i = 0; i < a.items.length; i++) {
if (!deepEqual(a.items[i], other.items[i])) return false;
}
return true;
}
default:
return JSON.stringify(a) === JSON.stringify(b);
}
}
function listToArray(value: Val): Val[] {
const items: Val[] = [];
let current = value;
while (current.tag === "Vector" && current.items.length >= 2) {
items.push(current.items[0]);
current = current.items[1];
}
return items;
}
function arrayToList(items: Val[]): Val {
let result: Val = VUnit;
for (let i = items.length - 1; i >= 0; i--) {
result = { tag: "Vector", items: [items[i], result] };
}
return result;
}
export function installMembranePrims(store: Store): { env: Env; store: Store } {
let env: Env = envEmpty();
let st: Store = store;
function def(name: string, v: Val) {
const [nextStore, addr] = st.alloc(v);
st = nextStore;
env = envSet(env, name, addr);
}
function isCallable(proc: Val): proc is Val {
return proc.tag === "Native" || proc.tag === "Closure";
}
function ensureArity(proc: Val, expected: number, name: string): void {
if (proc.tag === "Native") {
if (proc.arity !== "variadic" && proc.arity !== expected) {
throw new Error(`${name}: expected procedure of arity ${expected}`);
}
return;
}
if (proc.tag === "Closure") {
if (proc.params.length !== expected) {
throw new Error(`${name}: expected procedure of arity ${expected}`);
}
return;
}
throw new Error(`${name}: expected a procedure`);
}
function applyProcedure(proc: Val, procArgs: Val[], state: State): State | StepOutcome {
if (proc.tag === "Native") {
return proc.fn(procArgs, state);
}
if (proc.tag === "Closure") {
if (proc.params.length !== procArgs.length) {
throw new Error(`procedure arity mismatch: expected ${proc.params.length}, got ${procArgs.length}`);
}
let nextStore = state.store;
let nextEnv = proc.env;
for (let i = 0; i < proc.params.length; i++) {
const [allocatedStore, addr] = nextStore.alloc(procArgs[i]);
nextStore = allocatedStore;
nextEnv = envSet(nextEnv, proc.params[i], addr);
}
const kont = state.kont.concat([{ tag: "KCall", savedEnv: state.env } as Frame]);
return { ...state, control: { tag: "Expr", e: proc.body }, env: nextEnv, store: nextStore, kont };
}
throw new Error("expected procedure");
}
function applyNative(proc: Val, procArgs: Val[], state: State): Val | null {
if (proc.tag !== "Native") {
return null;
}
const result = proc.fn(procArgs, state);
if ("control" in result && result.control.tag === "Val") {
return result.control.v;
}
return null;
}
registerConditionPrims(def, { applyProcedure, ensureArity, isCallable });
def("*uninit*", { tag: "Native", name: "*uninit*", arity: 0, fn: (_args, s) => {
return { ...s, control: { tag: "Val", v: VUnit } };
}});
def("+", { tag: "Native", name: "+", arity: "variadic", fn: (args, s) => {
return { ...s, control: { tag: "Val", v: { tag: "Num", n: args.reduce((acc, value) => acc + (value as { n: number }).n, 0) } } };
}});
def("-", { tag: "Native", name: "-", arity: "variadic", fn: (args, s) => {
const nums = args.map((value) => (value as { n: number }).n);
const n = nums.length === 1 ? -nums[0] : nums.slice(1).reduce((acc, value) => acc - value, nums[0]);
return { ...s, control: { tag: "Val", v: { tag: "Num", n } } };
}});
def("*", { tag: "Native", name: "*", arity: "variadic", fn: (args, s) => {
return { ...s, control: { tag: "Val", v: { tag: "Num", n: args.reduce((acc, value) => acc * (value as { n: number }).n, 1) } } };
}});
def("=", { tag: "Native", name: "=", arity: 2, fn: (args, s) => {
return { ...s, control: { tag: "Val", v: (args[0] as { n: number }).n === (args[1] as { n: number }).n ? VTrue : VFalse } };
}});
def("<", { tag: "Native", name: "<", arity: 2, fn: (args, s) => {
return { ...s, control: { tag: "Val", v: (args[0] as { n: number }).n < (args[1] as { n: number }).n ? VTrue : VFalse } };
}});
def(">", { tag: "Native", name: ">", arity: 2, fn: (args, s) => {
return { ...s, control: { tag: "Val", v: (args[0] as { n: number }).n > (args[1] as { n: number }).n ? VTrue : VFalse } };
}});
def("<=", { tag: "Native", name: "<=", arity: 2, fn: (args, s) => {
return { ...s, control: { tag: "Val", v: (args[0] as { n: number }).n <= (args[1] as { n: number }).n ? VTrue : VFalse } };
}});
def(">=", { tag: "Native", name: ">=", arity: 2, fn: (args, s) => {
return { ...s, control: { tag: "Val", v: (args[0] as { n: number }).n >= (args[1] as { n: number }).n ? VTrue : VFalse } };
}});
def("/", { tag: "Native", name: "/", arity: 2, fn: (args, s) => {
return { ...s, control: { tag: "Val", v: { tag: "Num", n: (args[0] as { n: number }).n / (args[1] as { n: number }).n } } };
}});
def("modulo", { tag: "Native", name: "modulo", arity: 2, fn: (args, s) => {
return { ...s, control: { tag: "Val", v: { tag: "Num", n: (args[0] as { n: number }).n % (args[1] as { n: number }).n } } };
}});
def("not", { tag: "Native", name: "not", arity: 1, fn: (args, s) => {
const value = args[0];
const isFalsey = (value.tag === "Bool" && !value.b) || value.tag === "Unit";
return { ...s, control: { tag: "Val", v: isFalsey ? VTrue : VFalse } };
}});
def("or", { tag: "Native", name: "or", arity: "variadic", fn: (args, s) => {
for (const arg of args) {
const truthy = !(arg.tag === "Bool" && !arg.b) && arg.tag !== "Unit";
if (truthy) {
return { ...s, control: { tag: "Val", v: arg } };
}
}
return { ...s, control: { tag: "Val", v: VFalse } };
}});
def("and", { tag: "Native", name: "and", arity: "variadic", fn: (args, s) => {
let last: Val = VTrue;
for (const arg of args) {
const truthy = !(arg.tag === "Bool" && !arg.b) && arg.tag !== "Unit";
if (!truthy) {
return { ...s, control: { tag: "Val", v: arg } };
}
last = arg;
}
return { ...s, control: { tag: "Val", v: last } };
}});
def("cons", { tag: "Native", name: "cons", arity: 2, fn: (args, s) => {
return { ...s, control: { tag: "Val", v: { tag: "Vector", items: [args[0], args[1]] } } };
}});
def("car", { tag: "Native", name: "car", arity: 1, fn: (args, s) => {
const value = args[0] as { tag: string; items?: Val[] };
if (value.tag === "Vector" && value.items && value.items.length >= 1) {
return { ...s, control: { tag: "Val", v: value.items[0] } };
}
throw new Error("car: expected pair");
}});
def("cdr", { tag: "Native", name: "cdr", arity: 1, fn: (args, s) => {
const value = args[0] as { tag: string; items?: Val[] };
if (value.tag === "Vector" && value.items && value.items.length >= 2) {
return { ...s, control: { tag: "Val", v: value.items[1] } };
}
throw new Error("cdr: expected pair");
}});
def("null?", { tag: "Native", name: "null?", arity: 1, fn: (args, s) => {
const value = args[0] as { tag: string; items?: Val[] };
const isNull = value.tag === "Unit" || (value.tag === "Vector" && value.items?.length === 0);
return { ...s, control: { tag: "Val", v: isNull ? VTrue : VFalse } };
}});
def("pair?", { tag: "Native", name: "pair?", arity: 1, fn: (args, s) => {
const value = args[0] as { tag: string; items?: Val[] };
const isPair = value.tag === "Vector" && (value.items?.length ?? 0) >= 2;
return { ...s, control: { tag: "Val", v: isPair ? VTrue : VFalse } };
}});
def("list?", { tag: "Native", name: "list?", arity: 1, fn: (args, s) => {
const value = args[0] as { tag: string; items?: Val[] };
const isNull = value.tag === "Unit" || (value.tag === "Vector" && value.items?.length === 0);
const isPair = value.tag === "Vector" && (value.items?.length ?? 0) >= 2;
return { ...s, control: { tag: "Val", v: isNull || isPair ? VTrue : VFalse } };
}});
def("list", { tag: "Native", name: "list", arity: "variadic", fn: (args, s) => {
return { ...s, control: { tag: "Val", v: arrayToList(args) } };
}});
def("reverse", { tag: "Native", name: "reverse", arity: 1, fn: (args, s) => {
const items = listToArray(args[0]);
let result: Val = VUnit;
for (const item of items) {
result = { tag: "Vector", items: [item, result] };
}
return { ...s, control: { tag: "Val", v: result } };
}});
def("string-length", { tag: "Native", name: "string-length", arity: 1, fn: (args, s) => {
const value = args[0] as { tag: string; s?: string };
if (value.tag !== "Str") throw new Error("string-length: expected string");
return { ...s, control: { tag: "Val", v: { tag: "Num", n: value.s!.length } } };
}});
def("string-append", { tag: "Native", name: "string-append", arity: "variadic", fn: (args, s) => {
let result = "";
for (const arg of args) {
if (arg.tag !== "Str") throw new Error("string-append: expected strings");
result += arg.s;
}
return { ...s, control: { tag: "Val", v: { tag: "Str", s: result } } };
}});
def("substring", { tag: "Native", name: "substring", arity: 3, fn: (args, s) => {
const str = args[0];
const start = args[1];
const end = args[2];
if (str.tag !== "Str") throw new Error("substring: expected string");
if (start.tag !== "Num" || end.tag !== "Num") throw new Error("substring: expected numbers for indices");
return { ...s, control: { tag: "Val", v: { tag: "Str", s: str.s.substring(start.n, end.n) } } };
}});
def("get", { tag: "Native", name: "get", arity: "variadic", fn: (args, s) => {
if (args.length < 2 || args.length > 3) throw new Error("get: expected (get coll key) or (get coll key default)");
const coll = args[0];
const key = args[1];
const defaultVal: Val = args.length === 3 ? args[2] : VUnit;
if (coll.tag === "Map") {
for (const [entryKey, entryValue] of coll.entries) {
if (deepEqual(entryKey, key)) {
return { ...s, control: { tag: "Val", v: entryValue } };
}
}
return { ...s, control: { tag: "Val", v: defaultVal } };
}
if (coll.tag === "Vector" && key.tag === "Num") {
if (key.n >= 0 && key.n < coll.items.length) {
return { ...s, control: { tag: "Val", v: coll.items[key.n] } };
}
return { ...s, control: { tag: "Val", v: defaultVal } };
}
return { ...s, control: { tag: "Val", v: defaultVal } };
}});
def("equal?", { tag: "Native", name: "equal?", arity: 2, fn: (args, s) => {
return { ...s, control: { tag: "Val", v: deepEqual(args[0], args[1]) ? VTrue : VFalse } };
}});
def("map", { tag: "Native", name: "map", arity: 2, fn: (args, s) => {
const proc = args[0];
const items = listToArray(args[1]);
if (items.length === 0) {
return { ...s, control: { tag: "Val", v: VUnit } };
}
if (proc.tag === "Native") {
const results: Val[] = [];
for (const item of items) {
const result = applyNative(proc, [item], s);
if (result === null) throw new Error("map: Native function did not return a value");
results.push(result);
}
return { ...s, control: { tag: "Val", v: arrayToList(results) } };
}
if (proc.tag === "Closure") {
const [first, ...rest] = items;
const frame: Frame = { tag: "KMapRest", fn: proc, remaining: rest, acc: [], env: s.env };
return applyProcedure(proc, [first], { ...s, kont: [...s.kont, frame] });
}
throw new Error("map: expected procedure");
}});
def("filter", { tag: "Native", name: "filter", arity: 2, fn: (args, s) => {
const proc = args[0];
const items = listToArray(args[1]);
if (items.length === 0) {
return { ...s, control: { tag: "Val", v: VUnit } };
}
if (proc.tag === "Native") {
const results: Val[] = [];
for (const item of items) {
const result = applyNative(proc, [item], s);
if (result === null) throw new Error("filter: Native function did not return a value");
if ((result.tag === "Bool" && result.b) || (result.tag !== "Bool" && result.tag !== "Unit")) {
results.push(item);
}
}
return { ...s, control: { tag: "Val", v: arrayToList(results) } };
}
if (proc.tag === "Closure") {
const [first, ...rest] = items;
const frame: Frame = { tag: "KFilterRest", fn: proc, remaining: rest, currentItem: first, acc: [], env: s.env };
return applyProcedure(proc, [first], { ...s, kont: [...s.kont, frame] });
}
throw new Error("filter: expected procedure");
}});
return { env, store: st };
}