diff --git a/docs/generated/minlam.md b/docs/generated/minlam.md
index b35cad1..b8eb005 100644
--- a/docs/generated/minlam.md
+++ b/docs/generated/minlam.md
@@ -7,6 +7,7 @@ flowchart LR
MinExpTable --entries--> MinExp
MinLam --args--> SymbolList
MinLam --exp--> MinExp
+MinLam --cc--> bool
MinExprList --exp--> MinExp
MinExprList --next--> MinExprList
MinPrimApp --type--> MinPrimOp
@@ -15,6 +16,7 @@ MinPrimApp --exp2--> MinExp
MinApply --function--> MinExp
MinApply --args--> MinExprList
MinApply --isBuiltin--> bool
+MinApply --cc--> bool
MinIff --condition--> MinExp
MinIff --consequent--> MinExp
MinIff --alternative--> MinExp
@@ -53,6 +55,7 @@ MinExp --bindings--> MinBindings
MinExp --callCC--> MinExp
MinExp --character--> character
MinExp --cond--> MinCond
+MinExp --done--> void_ptr
MinExp --iff--> MinIff
MinExp --lam--> MinLam
MinExp --letRec--> MinLetRec
diff --git a/docs/generated/utils.md b/docs/generated/utils.md
index 2213d4d..d2dc10f 100644
--- a/docs/generated/utils.md
+++ b/docs/generated/utils.md
@@ -16,6 +16,7 @@ SymbolEnv --parent--> SymbolEnv
SymbolEnv --bindings--> SymbolSet
SCharVec["(SCharVec)"] --entries--> schar
WCharVec["(WCharVec)"] --entries--> character
+SymbolVec["(SymbolVec)"] --entries--> HashSymbol
StringArray["StringArray[]"] --entries--> string
WCharArray["WCharArray[]"] --entries--> character
UCharArray["UCharArray[]"] --entries--> byte
diff --git a/fn/rewrite/closure-convert.fn b/fn/rewrite/closure-convert.fn
index bc58d62..ea7bff8 100644
--- a/fn/rewrite/closure-convert.fn
+++ b/fn/rewrite/closure-convert.fn
@@ -10,6 +10,17 @@ import list operator "_|>_";
// https://matt.might.net/articles/closure-conversion/
+// How to avoid having to declare extra types just to support closure conversion?
+// I'm thinking make_env is obviously just make_vec, likewise make_closure.
+// env_ref becomes vec, lambdac becomes lambda.
+// The `(VEC index vector)` needs to know the index of the var in the vector.
+// That can work if we keep an additional IntMap from fv to index, and sort
+// by that when creating the make_vec env.
+// apply closure is just `(λ (vec . args) (apply (VEC 0 vec) (cons (VEC 1 vec) args)))`
+// How to optimize that?
+// minApplyClosure(vec, args) |-->
+// newMinApply(newMinPrimApp(MINPRIMOP_TYPE_VEC, 0, vec),
+// newMinExprList(newMinPrimApp(MINPRIMOP_TYPE_VEC, 1, vec), args))
fn closure_convert {
(exp=M.lambda(params, body)) {
let
diff --git a/fn/rewrite/test_harness.fn b/fn/rewrite/test_harness.fn
index 6c83234..d57eadf 100644
--- a/fn/rewrite/test_harness.fn
+++ b/fn/rewrite/test_harness.fn
@@ -12,6 +12,7 @@ let
link "closure-convert.fn" as CC;
link "curry.fn" as C;
link "uncurry.fn" as U;
+ link "transform.fn" as TR;
in
list.for_each(fn {
(';' @ s) {
@@ -61,4 +62,5 @@ in
M.print_expr(h);
puts("\n\n")
}
- }, Samples.data());
\ No newline at end of file
+ }, Samples.data());
+ print typeof(TR._transform);
\ No newline at end of file
diff --git a/fn/rewrite/transform.fn b/fn/rewrite/transform.fn
index a12352d..75053fb 100644
--- a/fn/rewrite/transform.fn
+++ b/fn/rewrite/transform.fn
@@ -4,22 +4,22 @@ link "minexpr.fn" as M;
link "../listutils.fn" as list;
import list operators;
-// (M.expr -> M.expr) -> M.expr -> M.expr
+// (exp -> exp) -> exp -> exp
fn bottom_up(f, exp) {
- f(_transform(bottom_up(f), exp))
+ f(_transform(bottom_up, f, exp))
}
-// (M.expr -> M.expr) -> M.expr -> M.expr
+// (exp -> exp) -> exp -> exp
fn top_down(f, exp) {
- _transform(top_down(f), f(exp))
+ _transform(top_down, f, f(exp))
}
-// (M.expr -> M.expr) -> M.expr -> M.expr
-fn _transform(t, exp) {
+// ((exp -> exp) -> exp -> exp) -> (exp -> exp) -> exp -> exp
+fn _transform(t, a, exp) {
switch (exp) {
(M.amb_expr(e1, e2)) {
- M.amb_expr(t(e1), t(e2))
+ M.amb_expr(t(a, e1), t(a, e2))
}
(M.back_expr) |
@@ -31,59 +31,59 @@ fn _transform(t, exp) {
(M.apply_closure(fun, args)) {
- M.apply_closure(t(fun), args |> t)
+ M.apply_closure(t(a, fun), args |> t(a))
}
(M.apply(fun, args)) {
- M.apply(t(fun), args |> t)
+ M.apply(t(a, fun), args |> t(a))
}
(M.callcc_expr(e)) {
- M.callcc_expr(t(e))
+ M.callcc_expr(t(a, e))
}
(M.cond_expr(test, branches)) {
- M.cond_expr(t(test), branches |> t && t)
+ M.cond_expr(t(a, test), branches |> t(a) && t(a))
}
(M.env_ref(e, s)) {
- M.env_ref(t(e), s)
+ M.env_ref(t(a, e), s)
}
(M.if_expr(test, consequent, alternative)) {
- M.if_expr(t(test), t(consequent), t(alternative))
+ M.if_expr(t(a, test), t(a, consequent), t(a, alternative))
}
(M.lambda(params, body)) {
- M.lambda(params, t(body))
+ M.lambda(params, t(a, body))
}
(M.lambdac(params, body)) {
- M.lambdac(params, t(body))
+ M.lambdac(params, t(a, body))
}
(M.letrec_expr(bindings, body)) {
- M.letrec_expr(bindings |> identity && (t && identity), t(body))
+ M.letrec_expr(bindings |> identity && (t(a) && identity), t(a, body))
}
(M.make_closure(lam, env)) {
- M.make_closure(t(lam), t(env))
+ M.make_closure(t(a, lam), t(a, env))
}
(M.make_env(bindings)) {
- M.make_env(bindings |> identity && t)
+ M.make_env(bindings |> identity && t(a))
}
(M.make_vec(size, elements)) {
- M.make_vec(size, elements |> t)
+ M.make_vec(size, elements |> t(a))
}
(M.match_cases(e, cases)) {
- M.match_cases(t(e), cases |> identity && t)
+ M.match_cases(t(a, e), cases |> identity && t(a))
}
(M.sequence(exps)) {
- M.sequence(exps |> t)
+ M.sequence(exps |> t(a))
}
(_) {
diff --git a/src/lambda_desugar.c b/src/lambda_desugar.c
index 65d61d9..0312f2a 100644
--- a/src/lambda_desugar.c
+++ b/src/lambda_desugar.c
@@ -44,6 +44,7 @@ static MinExp *desugarLamMakeTuple(LamExp *);
static MinExp *desugarLamTag(LamExp *);
static MinExp *desugarLamTupleIndex(LamExp *);
static MinExp *desugarLamTypeDefs(LamExp *);
+static MinExp *_desugarLamExp(LamExp *);
static MinLam *desugarLamLam(LamLam *node);
static SymbolList *desugarLamVarList(SymbolList *node);
@@ -133,9 +134,9 @@ static MinPrimApp *desugarLamPrimApp(LamPrimApp *node) {
return NULL;
}
- MinExp *new_exp1 = desugarLamExp(node->exp1);
+ MinExp *new_exp1 = _desugarLamExp(node->exp1);
int save = PROTECT(new_exp1);
- MinExp *new_exp2 = desugarLamExp(node->exp2);
+ MinExp *new_exp2 = _desugarLamExp(node->exp2);
PROTECT(new_exp2);
MinPrimApp *result = newMinPrimApp(CPI(node), desugarLamPrimOp(node->type),
new_exp1, new_exp2);
@@ -153,7 +154,7 @@ static MinLam *desugarLamLam(LamLam *node) {
SymbolList *params = desugarLamVarList(node->args);
int save = PROTECT(params);
- MinExp *body = desugarLamExp(node->exp);
+ MinExp *body = _desugarLamExp(node->exp);
PROTECT(body);
MinLam *result = newMinLam(CPI(node), params, body);
UNPROTECT(save);
@@ -170,7 +171,7 @@ static MinExprList *desugarLamSequence(LamSequence *node) {
MinExprList *next = desugarLamSequence(node->next);
int save = PROTECT(next);
- MinExp *exp = desugarLamExp(node->exp);
+ MinExp *exp = _desugarLamExp(node->exp);
PROTECT(exp);
MinExprList *result = newMinExprList(CPI(node), exp, next);
UNPROTECT(save);
@@ -187,7 +188,7 @@ static MinExprList *desugarLamArgs(LamArgs *node) {
MinExprList *next = desugarLamArgs(node->next);
int save = PROTECT(next);
- MinExp *exp = desugarLamExp(node->exp);
+ MinExp *exp = _desugarLamExp(node->exp);
PROTECT(exp);
MinExprList *result = newMinExprList(CPI(node), exp, next);
UNPROTECT(save);
@@ -202,7 +203,7 @@ static MinApply *desugarLamApply(LamApply *node) {
return NULL;
}
- MinExp *function = desugarLamExp(node->function);
+ MinExp *function = _desugarLamExp(node->function);
int save = PROTECT(function);
MinExprList *args = desugarLamArgs(node->args);
PROTECT(args);
@@ -303,11 +304,11 @@ static MinIff *desugarLamIff(LamIff *node) {
return NULL;
}
- MinExp *condition = desugarLamExp(node->condition);
+ MinExp *condition = _desugarLamExp(node->condition);
int save = PROTECT(condition);
- MinExp *consequent = desugarLamExp(node->consequent);
+ MinExp *consequent = _desugarLamExp(node->consequent);
PROTECT(consequent);
- MinExp *alternative = desugarLamExp(node->alternative);
+ MinExp *alternative = _desugarLamExp(node->alternative);
PROTECT(alternative);
MinIff *result = newMinIff(CPI(node), condition, consequent, alternative);
UNPROTECT(save);
@@ -322,7 +323,7 @@ static MinCond *desugarLamCond(LamCond *node) {
return NULL;
}
- MinExp *value = desugarLamExp(node->value);
+ MinExp *value = _desugarLamExp(node->value);
int save = PROTECT(value);
MinCondCases *cases = desugarLamCondCases(node->cases);
PROTECT(cases);
@@ -339,7 +340,7 @@ static MinIntCondCases *desugarLamIntCondCases(LamIntCondCases *node) {
return NULL;
}
- MinExp *body = desugarLamExp(node->body);
+ MinExp *body = _desugarLamExp(node->body);
int save = PROTECT(body);
MinIntCondCases *next = desugarLamIntCondCases(node->next);
PROTECT(next);
@@ -357,7 +358,7 @@ static MinCharCondCases *desugarLamCharCondCases(LamCharCondCases *node) {
return NULL;
}
- MinExp *body = desugarLamExp(node->body);
+ MinExp *body = _desugarLamExp(node->body);
int save = PROTECT(body);
MinCharCondCases *next = desugarLamCharCondCases(node->next);
PROTECT(next);
@@ -375,7 +376,7 @@ static MinMatch *desugarLamMatch(LamMatch *node) {
return NULL;
}
- MinExp *index = desugarLamExp(node->index);
+ MinExp *index = _desugarLamExp(node->index);
int save = PROTECT(index);
MinMatchList *cases = desugarLamMatchList(node->cases);
PROTECT(cases);
@@ -394,7 +395,7 @@ static MinMatchList *desugarLamMatchList(LamMatchList *node) {
MinIntList *matches = desugarLamIntList(node->matches);
int save = PROTECT(matches);
- MinExp *body = desugarLamExp(node->body);
+ MinExp *body = _desugarLamExp(node->body);
PROTECT(body);
MinMatchList *next = desugarLamMatchList(node->next);
PROTECT(next);
@@ -452,7 +453,7 @@ static MinExp *desugarLamLet(LamExp *exp) {
LamLet *node = getLamExp_Let(exp);
MinBindings *bindings = desugarLamBindings(node->bindings);
int save = PROTECT(bindings);
- MinExp *body = desugarLamExp(node->body);
+ MinExp *body = _desugarLamExp(node->body);
PROTECT(body);
SymbolList *fargs = extractKeysFromBindings(bindings);
PROTECT(fargs);
@@ -475,7 +476,7 @@ static MinLetRec *desugarLamLetRec(LamLetRec *node) {
MinBindings *bindings = desugarLamBindings(node->bindings);
int save = PROTECT(bindings);
- MinExp *body = desugarLamExp(node->body);
+ MinExp *body = _desugarLamExp(node->body);
PROTECT(body);
MinLetRec *result = newMinLetRec(CPI(node), bindings, body);
UNPROTECT(save);
@@ -506,7 +507,7 @@ static MinExp *desugarLamLetStar(LamExp *exp) {
// build a nest of lets, then desugar that
LamExp *lets = nestLets(node->bindings, node->body);
int save = PROTECT(lets);
- MinExp *result = desugarLamExp(lets);
+ MinExp *result = _desugarLamExp(lets);
UNPROTECT(save);
LEAVE(desugarLamLetStar);
return result;
@@ -519,7 +520,7 @@ static MinBindings *desugarLamBindings(LamBindings *node) {
return NULL;
}
- MinExp *val = desugarLamExp(node->val);
+ MinExp *val = _desugarLamExp(node->val);
int save = PROTECT(val);
MinBindings *next = desugarLamBindings(node->next);
PROTECT(next);
@@ -542,9 +543,9 @@ static MinAmb *desugarLamAmb(LamAmb *node) {
return NULL;
}
- MinExp *left = desugarLamExp(node->left);
+ MinExp *left = _desugarLamExp(node->left);
int save = PROTECT(left);
- MinExp *right = desugarLamExp(node->right);
+ MinExp *right = _desugarLamExp(node->right);
PROTECT(right);
MinAmb *result = newMinAmb(CPI(node), left, right);
UNPROTECT(save);
@@ -555,22 +556,22 @@ static MinAmb *desugarLamAmb(LamAmb *node) {
static MinExp *desugarLamTypeOf(LamExp *exp) {
ENTER(desugarLamTypeOf);
LamTypeOf *node = getLamExp_TypeOf(exp);
- MinExp *result = desugarLamExp(node->typeString);
+ MinExp *result = _desugarLamExp(node->typeString);
LEAVE(desugarLamTypeOf);
return result;
}
static MinExp *desugarLamTypeDefs(LamExp *exp) {
ENTER(desugarLamTypeDefs);
- MinExp *result = desugarLamExp(getLamExp_TypeDefs(exp)->body);
+ MinExp *result = _desugarLamExp(getLamExp_TypeDefs(exp)->body);
LEAVE(desugarLamTypeDefs);
return result;
}
static MinExp *desugarLamPrint(LamExp *node) {
- MinExp *printer = desugarLamExp(getLamExp_Print(node)->printer);
+ MinExp *printer = _desugarLamExp(getLamExp_Print(node)->printer);
int save = PROTECT(printer);
- MinExp *arg = desugarLamExp(getLamExp_Print(node)->exp);
+ MinExp *arg = _desugarLamExp(getLamExp_Print(node)->exp);
PROTECT(arg);
MinExprList *args = newMinExprList(CPI(node), arg, NULL);
PROTECT(args);
@@ -655,7 +656,9 @@ static MinCondCases *desugarLamCondCases(LamCondCases *node) {
}
// Main desugaring function and public interface
-MinExp *desugarLamExp(LamExp *node) {
+MinExp *desugarLamExp(LamExp *node) { return _desugarLamExp(node); }
+
+static MinExp *_desugarLamExp(LamExp *node) {
ENTER(desugarLamExp);
if (node == NULL) {
LEAVE(desugarLamExp);
@@ -699,7 +702,7 @@ MinExp *desugarLamExp(LamExp *node) {
}
case LAMEXP_TYPE_CALLCC: {
// LamExp
- MinExp *new_callcc = desugarLamExp(getLamExp_CallCC(node));
+ MinExp *new_callcc = _desugarLamExp(getLamExp_CallCC(node));
PROTECT(new_callcc);
result = newMinExp_CallCC(CPI(node), new_callcc);
break;
@@ -767,7 +770,7 @@ MinExp *desugarLamExp(LamExp *node) {
LamPrimApp *prim = getLamExp_Prim(node);
if (prim->replacement != NULL) {
// Use the replacement instead of the primitive
- result = desugarLamExp(prim->replacement);
+ result = _desugarLamExp(prim->replacement);
} else {
MinPrimApp *new = desugarLamPrimApp(prim);
PROTECT(new);
diff --git a/src/main.c b/src/main.c
index 7b31b35..d52357b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -46,6 +46,7 @@
#include "memory.h"
#include "minlam_alphaconvert.h"
#include "minlam_beta.h"
+#include "minlam_closureConvert.h"
#include "minlam_curry.h"
#include "minlam_eta.h"
#include "minlam_fold.h"
@@ -564,16 +565,6 @@ int main(int argc, char *argv[]) {
#endif
minExp = alphaConvertMinExp(minExp, builtIns);
REPLACE_PROTECT(save2, minExp);
-#ifdef TEST_CPS
- LamExp *halt = newLamExp_Var(CPI(exp), newSymbol("halt"));
- PROTECT(halt);
- forceGcFlag = true;
- exp = cpsTc(exp, halt);
- REPLACE_PROTECT(save2, exp);
- ppLamExp(exp);
- eprintf("\n");
- exit(0);
-#endif
if (alpha_flag) {
ppMinExp(minExp);
@@ -606,12 +597,26 @@ int main(int argc, char *argv[]) {
exit(0);
}
+#ifdef TEST_CPS
+ MinExp *done = makeDoneCont(CPI(exp));
+ PROTECT(done);
+ minExp = cpsTc(minExp, done);
+ REPLACE_PROTECT(save2, minExp);
+ minExp = sharedClosureConvert(minExp);
+ // minExp = flatClosureConvert(minExp);
+ REPLACE_PROTECT(save2, minExp);
+ ppMinExp(minExp);
+ eprintf("\n");
+ exit(0);
+#endif
+
AnfExp *anfExp = anfNormalize(minExp);
REPLACE_PROTECT(save2, anfExp);
if (anf_flag) {
ppAnfExp(anfExp);
eprintf("\n");
+ exit(0);
}
annotate(anfExp, builtIns);
diff --git a/src/minlam.yaml b/src/minlam.yaml
index 24666c9..8c005a4 100644
--- a/src/minlam.yaml
+++ b/src/minlam.yaml
@@ -35,6 +35,7 @@ structs:
data:
args: SymbolList
exp: MinExp
+ cc: bool=false
MinExprList:
meta:
@@ -58,6 +59,7 @@ structs:
function: MinExp
args: MinExprList
isBuiltin: bool=false
+ cc: bool=false
MinIff:
meta:
@@ -190,6 +192,7 @@ unions:
callCC: MinExp
character: character
cond: MinCond
+ done: void_ptr
iff: MinIff
lam: MinLam
letRec: MinLetRec
diff --git a/src/minlam_closureConvert.c b/src/minlam_closureConvert.c
new file mode 100644
index 0000000..88617f6
--- /dev/null
+++ b/src/minlam_closureConvert.c
@@ -0,0 +1,224 @@
+/*
+ * CEKF - VM supporting amb
+ * Copyright (C) 2022-2026 Bill Hails
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Minimal AST after desugaring
+ * Generated from src/minlam.yaml by tools/generate.py
+ */
+
+#include "minlam_closureConvert.h"
+#include "memory.h"
+#include "minlam.h"
+#include "minlam_freeVars.h"
+#include "minlam_subst.h"
+#include "minlam_transform.h"
+#include "symbol.h"
+#include "utils_helper.h"
+
+#ifdef DEBUG_MINLAM_CLOSURECONVERT
+#include "debugging_on.h"
+#else
+#include "debugging_off.h"
+#endif
+
+static MinExp *closureConvertMinLam(MinLam *node);
+static MinExp *closureConvertMinApply(MinApply *node);
+static MinExp *closureConvertMinExp(MinExp *node);
+
+//////////////
+// Public API
+//////////////
+
+MinExp *flatClosureConvert(MinExp *exp) {
+ return bottomUpMinExp(closureConvertMinExp, exp);
+}
+
+MinExp *sharedClosureConvert(MinExp *exp) {
+ return topDownMinExp(closureConvertMinExp, exp);
+}
+
+////////////////////
+// Helper utilities
+////////////////////
+
+static MinExp *makeMinEnvRef(ParserInfo I, HashSymbol *senv, int i) {
+ MinExp *index = newMinExp_Stdint(I, i);
+ int save = PROTECT(index);
+ MinExp *env = newMinExp_Var(I, senv);
+ PROTECT(env);
+ MinExp *result = makeMinExp_Prim(I, MINPRIMOP_TYPE_VEC, index, env);
+ UNPROTECT(save);
+ return result;
+}
+
+static void populateEnvReferences(MinExpTable *references, HashSymbol *env,
+ SymbolList *list) {
+ int i = 0;
+ int save = PROTECT(references); // claim a stack slot
+ while (list != NULL) {
+ MinExp *ref = makeMinEnvRef(CPI(list), env, i++);
+ REPLACE_PROTECT(save, ref);
+ setMinExpTable(references, list->symbol, ref);
+ list = list->next;
+ }
+ UNPROTECT(save);
+}
+
+static MinExprList *makeExprList(SymbolList *symbols) {
+ if (symbols == NULL)
+ return NULL;
+ MinExprList *rest = makeExprList(symbols->next);
+ int save = PROTECT(rest);
+ MinExp *var = newMinExp_Var(CPI(symbols), symbols->symbol);
+ PROTECT(var);
+ MinExprList *this = newMinExprList(CPI(var), var, rest);
+ UNPROTECT(save);
+ return this;
+}
+
+static MinExp *makeEnv(ParserInfo PI, SymbolList *symbols) {
+ MinExprList *list = makeExprList(symbols);
+ int save = PROTECT(list);
+ MinExp *env = newMinExp_MakeVec(PI, list);
+ UNPROTECT(save);
+ return env;
+}
+
+static MinExprList *makeClosure(MinExp *lambdac, MinExp *env) {
+ MinExprList *b = newMinExprList(CPI(env), env, NULL);
+ int save = PROTECT(b);
+ MinExprList *a = newMinExprList(CPI(lambdac), lambdac, b);
+ UNPROTECT(save);
+ return a;
+}
+
+static SymbolSet *freeVarsMinLam(MinLam *node) {
+ MinExp *lam = newMinExp_Lam(CPI(node), node);
+ int save = PROTECT(lam);
+ SymbolSet *fv = newSymbolSet();
+ PROTECT(fv);
+ freeVarsMinExp(lam, fv, NULL);
+ UNPROTECT(save);
+ return fv;
+}
+
+///////////////////////////
+// Visitor implementations
+///////////////////////////
+
+// (exp=M.lambda(params, body)) {
+// let
+// senv = GS.genstring("$env");
+// fv = FV.free(exp);
+// venv = fv |> fn (v) { #(v, M.var(v)) };
+// sub = DICT.make(fv, fv |> fn (v) { M.env_ref(M.var(senv), v) });
+// vbody = SUBST.substitute(sub, body);
+// in
+// M.make_closure(M.lambdac(senv @ params, vbody), M.make_env(venv))
+// }
+static MinExp *closureConvertMinLam(MinLam *node) {
+ ENTER(closureConvertMinLam);
+ if (node->cc) {
+ LEAVE(closureConvertMinLam);
+ return newMinExp_Lam(CPI(node), node);
+ }
+ HashSymbol *envName = genSymDollar("env");
+ // get the free variables in the body of the lambda
+ SymbolSet *fv = freeVarsMinLam(node);
+ int save = PROTECT(fv);
+ // create an arbitrary fixed ordering for the free variables
+ SymbolList *indexes = symbolSetToList(CPI(node), fv);
+ PROTECT(indexes);
+ // map from vars to index lookups
+ MinExpTable *sub = newMinExpTable();
+ PROTECT(sub);
+ populateEnvReferences(sub, envName, indexes);
+ // replace free variables in the body with vec refs into env
+ MinExp *vbody = substMinExp(node->exp, sub);
+ PROTECT(vbody);
+ // construct the env from a vec
+ MinExp *env = makeEnv(CPI(node), indexes);
+ PROTECT(env);
+ // prepend env onto the lambda args
+ SymbolList *sparams = newSymbolList(CPI(node), envName, node->args);
+ PROTECT(sparams);
+ // build the new lambda
+ MinExp *lambdac = makeMinExp_Lam(CPI(node), sparams, vbody);
+ PROTECT(lambdac);
+ // set the lambdac flag
+ getMinExp_Lam(lambdac)->cc = true;
+ // wrap the lambda and the env in a 2-vec closure
+ MinExprList *closureArgs = makeClosure(lambdac, env);
+ PROTECT(closureArgs);
+ MinExp *result = newMinExp_MakeVec(CPI(closureArgs), closureArgs);
+ UNPROTECT(save);
+ LEAVE(closureConvertMinLam);
+ return result;
+}
+
+// newMinApply(newMinPrimApp(MINPRIMOP_TYPE_VEC, 0, vec),
+// newMinExprList(newMinPrimApp(MINPRIMOP_TYPE_VEC, 1, vec),
+// args))
+static MinExp *closureConvertMinApply(MinApply *node) {
+ if (node == NULL)
+ return NULL;
+ ENTER(closureConvertMinApply);
+ if (node->isBuiltin) {
+ LEAVE(closureConvertMinApply);
+ return newMinExp_Apply(CPI(node), node);
+ }
+ if (node->cc) {
+ LEAVE(closureConvertMinApply);
+ return newMinExp_Apply(CPI(node), node);
+ }
+ MinExp *vec = node->function;
+ MinExprList *args = node->args;
+ MinExp *zero = newMinExp_Stdint(CPI(node), 0);
+ int save = PROTECT(zero);
+ MinExp *function =
+ makeMinExp_Prim(CPI(node), MINPRIMOP_TYPE_VEC, zero, vec);
+ PROTECT(function);
+ MinExp *one = newMinExp_Stdint(CPI(node), 1);
+ PROTECT(one);
+ MinExp *vec_ref = makeMinExp_Prim(CPI(node), MINPRIMOP_TYPE_VEC, one, vec);
+ PROTECT(vec_ref);
+ MinExprList *new_args = newMinExprList(CPI(node), vec_ref, args);
+ PROTECT(new_args);
+ MinExp *result = makeMinExp_Apply(CPI(node), function, new_args);
+ getMinExp_Apply(result)->cc = true;
+ UNPROTECT(save);
+ LEAVE(closureConvertMinApply);
+ return result;
+}
+
+static MinExp *closureConvertMinExp(MinExp *node) {
+ if (node == NULL)
+ return NULL;
+ ENTER(closureConvertMinExp);
+ MinExp *result = node;
+ switch (node->type) {
+ case MINEXP_TYPE_APPLY:
+ result = closureConvertMinApply(getMinExp_Apply(node));
+ break;
+ case MINEXP_TYPE_LAM:
+ result = closureConvertMinLam(getMinExp_Lam(node));
+ break;
+ default:
+ break;
+ }
+ LEAVE(closureConvertMinExp);
+ return result;
+}
\ No newline at end of file
diff --git a/src/minlam_closureConvert.h b/src/minlam_closureConvert.h
new file mode 100644
index 0000000..9ee9cce
--- /dev/null
+++ b/src/minlam_closureConvert.h
@@ -0,0 +1,26 @@
+#ifndef cekf_minlam_closureConvert_h
+#define cekf_minlam_closureConvert_h
+/*
+ * CEKF - VM supporting amb
+ * Copyright (C) 2022-2026 Bill Hails
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "minlam.h"
+
+MinExp *flatClosureConvert(MinExp *exp);
+MinExp *sharedClosureConvert(MinExp *exp);
+
+#endif
diff --git a/src/minlam_freeVars.c b/src/minlam_freeVars.c
index 6d15aec..eac7e22 100644
--- a/src/minlam_freeVars.c
+++ b/src/minlam_freeVars.c
@@ -273,6 +273,7 @@ void freeVarsMinExp(MinExp *node, SymbolSet *result, SymbolEnv *context) {
case MINEXP_TYPE_BIGINTEGER:
case MINEXP_TYPE_CHARACTER:
case MINEXP_TYPE_STDINT:
+ case MINEXP_TYPE_DONE:
break;
case MINEXP_TYPE_BINDINGS:
cant_happen("encountered MinBindings");
@@ -310,7 +311,7 @@ void freeVarsMinExp(MinExp *node, SymbolSet *result, SymbolEnv *context) {
}
break;
default:
- cant_happen("unrecognized MinExp type %d", node->type);
+ cant_happen("unrecognized MinExp type %s", minExpTypeName(node->type));
}
LEAVE(freeVarsMinExp);
diff --git a/src/minlam_helper.c b/src/minlam_helper.c
index d03805b..0583bf0 100644
--- a/src/minlam_helper.c
+++ b/src/minlam_helper.c
@@ -17,6 +17,7 @@
*/
#include "minlam_helper.h"
+#include "symbol.h"
SymbolList *minBindingsToSymbolList(MinBindings *bindings) {
if (bindings == NULL) {
@@ -28,4 +29,14 @@ SymbolList *minBindingsToSymbolList(MinBindings *bindings) {
SymbolList *this = newSymbolList(CPI(bindings), bindings->var, next);
UNPROTECT(save);
return this;
+}
+
+MinExp *makeDoneCont(ParserInfo PI) {
+ MinExp *body = newMinExp_Done(PI);
+ int save = PROTECT(body);
+ SymbolList *args = newSymbolList(PI, newSymbol("_"), NULL);
+ PROTECT(args);
+ MinExp *lambda = makeMinExp_Lam(PI, args, body);
+ UNPROTECT(save);
+ return lambda;
}
\ No newline at end of file
diff --git a/src/minlam_helper.h b/src/minlam_helper.h
index 242b001..0c3dee3 100644
--- a/src/minlam_helper.h
+++ b/src/minlam_helper.h
@@ -22,5 +22,6 @@
#include "utils.h"
SymbolList *minBindingsToSymbolList(MinBindings *bindings);
+MinExp *makeDoneCont(ParserInfo);
#endif
diff --git a/src/minlam_pp.c b/src/minlam_pp.c
index a379e4b..7c3d8df 100644
--- a/src/minlam_pp.c
+++ b/src/minlam_pp.c
@@ -130,6 +130,9 @@ void ppMinExp(MinExp *exp) {
case MINEXP_TYPE_BACK:
eprintf("(back)");
break;
+ case MINEXP_TYPE_DONE:
+ eprintf("(done)");
+ break;
case MINEXP_TYPE_COND:
ppMinCond(getMinExp_Cond(exp));
break;
diff --git a/src/minlam_transform.c b/src/minlam_transform.c
new file mode 100644
index 0000000..d94c7d7
--- /dev/null
+++ b/src/minlam_transform.c
@@ -0,0 +1,431 @@
+/*
+ * CEKF - VM supporting amb
+ * Copyright (C) 2022-2026 Bill Hails
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Minimal AST after desugaring
+ * Generated from src/minlam.yaml by tools/generate.py
+ */
+
+#include "memory.h"
+#include "minlam.h"
+
+#include "minlam_transform.h"
+
+#ifdef DEBUG_MINLAM_TRANSFORM
+#include "debugging_on.h"
+#else
+#include "debugging_off.h"
+#endif
+
+// (MinExp -> MinExp) -> MinExp -> MinExp
+typedef MinExp *(*MinExpTransform)(MinExpFn, MinExp *);
+
+// Context here is like a curry of t(a) as a closure.
+typedef struct Context {
+ MinExpTransform t;
+ MinExpFn a;
+} Context;
+
+static MinExp *transformMinLam(MinLam *node, Context *c);
+static MinExp *transformMinApply(MinApply *node, Context *c);
+static MinExp *transformMinIff(MinIff *node, Context *c);
+static MinExp *transformMinCond(MinCond *node, Context *c);
+static MinExp *transformMinMatch(MinMatch *node, Context *c);
+static MinExp *transformMinLetRec(MinLetRec *node, Context *c);
+static MinExp *transformMinAmb(MinAmb *node, Context *c);
+static MinExp *transformMinMakeVec(MinExprList *node, Context *c);
+static MinExp *transformMinSequence(MinExprList *node, Context *c);
+static MinExp *transformMinExp(MinExp *node, Context *c);
+
+static MinExprList *transformMinExprList(MinExprList *node, Context *c);
+static MinIntCondCases *transformMinIntCondCases(MinIntCondCases *node,
+ Context *c);
+static MinCharCondCases *transformMinCharCondCases(MinCharCondCases *node,
+ Context *c);
+static MinMatchList *transformMinMatchList(MinMatchList *node, Context *c);
+static MinBindings *transformMinBindings(MinBindings *node, Context *c);
+static MinCondCases *transformMinCondCases(MinCondCases *node, Context *c);
+
+//////////////////////////
+// Support and public API
+//////////////////////////
+
+// fn top_down(f, exp) {
+// _transform(top_down, f, f(exp))
+// }
+MinExp *topDownMinExp(MinExpFn f, MinExp *exp) {
+ MinExp *new_exp = f(exp);
+ int save = PROTECT(new_exp);
+ Context c = {topDownMinExp, f};
+ MinExp *result = transformMinExp(new_exp, &c);
+ UNPROTECT(save);
+ return result;
+}
+
+// fn bottom_up(f, exp) {
+// f(_transform(bottom_up, f, exp))
+// }
+MinExp *bottomUpMinExp(MinExpFn f, MinExp *exp) {
+ Context c = {bottomUpMinExp, f};
+ MinExp *new_exp = transformMinExp(exp, &c);
+ int save = PROTECT(new_exp);
+ MinExp *result = f(new_exp);
+ UNPROTECT(save);
+ return result;
+}
+
+static inline MinExp *apply(Context *c, MinExp *node) {
+ return c->t(c->a, node);
+}
+
+///////////////////////////
+// Visitor implementations
+///////////////////////////
+
+// (M.lambda(params, body)) {
+// M.lambda(params, t(a, body))
+// }
+static MinExp *transformMinLam(MinLam *node, Context *c) {
+ if (node == NULL)
+ return NULL;
+ ENTER(transformMinLam);
+ MinExp *new_exp = apply(c, node->exp);
+ int save = PROTECT(new_exp);
+ MinExp *result = makeMinExp_Lam(CPI(node), node->args, new_exp);
+ UNPROTECT(save);
+ LEAVE(transformMinLam);
+ return result;
+}
+
+// args |> t(a)
+static MinExprList *transformMinExprList(MinExprList *node, Context *c) {
+ if (node == NULL)
+ return NULL;
+ ENTER(transformMinExprList);
+ MinExprList *new_next = transformMinExprList(node->next, c);
+ int save = PROTECT(new_next);
+ MinExp *new_exp = apply(c, node->exp);
+ PROTECT(new_exp);
+ MinExprList *result = newMinExprList(CPI(node), new_exp, new_next);
+ UNPROTECT(save);
+ LEAVE(transformMinExprList);
+ return result;
+}
+
+// (M.apply(fun, args)) {
+// M.apply(t(a, fun), args |> t(a))
+// }
+static MinExp *transformMinApply(MinApply *node, Context *c) {
+ if (node == NULL)
+ return NULL;
+ if (node->isBuiltin)
+ return newMinExp_Apply(CPI(node), node);
+ ENTER(transformMinApply);
+ MinExp *new_function = apply(c, node->function);
+ int save = PROTECT(new_function);
+ MinExprList *new_args = transformMinExprList(node->args, c);
+ PROTECT(new_args);
+ MinExp *result = makeMinExp_Apply(CPI(node), new_function, new_args);
+ getMinExp_Apply(result)->isBuiltin = node->isBuiltin;
+ UNPROTECT(save);
+ LEAVE(transformMinApply);
+ return result;
+}
+
+// (M.if_expr(test, consequent, alternative)) {
+// M.if_expr(t(a, test), t(a, consequent), t(a, alternative))
+// }
+static MinExp *transformMinIff(MinIff *node, Context *c) {
+ if (node == NULL)
+ return NULL;
+ ENTER(transformMinIff);
+ MinExp *new_condition = apply(c, node->condition);
+ int save = PROTECT(new_condition);
+ MinExp *new_consequent = apply(c, node->consequent);
+ PROTECT(new_consequent);
+ MinExp *new_alternative = apply(c, node->alternative);
+ PROTECT(new_alternative);
+ MinExp *result = makeMinExp_Iff(CPI(node), new_condition, new_consequent,
+ new_alternative);
+ UNPROTECT(save);
+ LEAVE(transformMinIff);
+ return result;
+}
+
+// (M.cond_expr(test, branches)) {
+// M.cond_expr(t(a, test), branches |> t(a) && t(a))
+// }
+static MinExp *transformMinCond(MinCond *node, Context *c) {
+ if (node == NULL)
+ return NULL;
+ ENTER(transformMinCond);
+ MinExp *new_value = apply(c, node->value);
+ int save = PROTECT(new_value);
+ MinCondCases *new_cases = transformMinCondCases(node->cases, c);
+ PROTECT(new_cases);
+ MinExp *result = makeMinExp_Cond(CPI(node), new_value, new_cases);
+ UNPROTECT(save);
+ LEAVE(transformMinCond);
+ return result;
+}
+
+// branches |> t(a) && t(a)
+static MinCondCases *transformMinCondCases(MinCondCases *node, Context *c) {
+ if (node == NULL)
+ return NULL;
+ ENTER(transformMinCondCases);
+ int save = PROTECT(NULL);
+ MinCondCases *result = node;
+ switch (node->type) {
+ case MINCONDCASES_TYPE_INTEGERS: {
+ MinIntCondCases *new_variant =
+ transformMinIntCondCases(getMinCondCases_Integers(node), c);
+ PROTECT(new_variant);
+ result = newMinCondCases_Integers(CPI(node), new_variant);
+ break;
+ }
+ case MINCONDCASES_TYPE_CHARACTERS: {
+ MinCharCondCases *new_variant =
+ transformMinCharCondCases(getMinCondCases_Characters(node), c);
+ PROTECT(new_variant);
+ result = newMinCondCases_Characters(CPI(node), new_variant);
+ break;
+ }
+ default:
+ cant_happen("unrecognized MinCondCases type %s",
+ minCondCasesTypeName(node->type));
+ }
+ UNPROTECT(save);
+ LEAVE(transformMinCondCases);
+ return result;
+}
+
+// branches |> t(a) && t(a)
+static MinIntCondCases *transformMinIntCondCases(MinIntCondCases *node,
+ Context *c) {
+ if (node == NULL)
+ return NULL;
+ ENTER(transformMinIntCondCases);
+ MinExp *new_body = apply(c, node->body);
+ int save = PROTECT(new_body);
+ MinIntCondCases *new_next = transformMinIntCondCases(node->next, c);
+ PROTECT(new_next);
+ MinIntCondCases *result =
+ newMinIntCondCases(CPI(node), node->constant, new_body, new_next);
+ UNPROTECT(save);
+ LEAVE(transformMinIntCondCases);
+ return result;
+}
+
+// branches |> t(a) && t(a)
+static MinCharCondCases *transformMinCharCondCases(MinCharCondCases *node,
+ Context *c) {
+ if (node == NULL)
+ return NULL;
+ ENTER(transformMinCharCondCases);
+ MinExp *new_body = apply(c, node->body);
+ int save = PROTECT(new_body);
+ MinCharCondCases *new_next = transformMinCharCondCases(node->next, c);
+ PROTECT(new_next);
+ MinCharCondCases *result =
+ newMinCharCondCases(CPI(node), node->constant, new_body, new_next);
+ UNPROTECT(save);
+ LEAVE(transformMinCharCondCases);
+ return result;
+}
+
+// (M.match_cases(e, cases)) {
+// M.match_cases(t(a, e), cases |> identity && t(a))
+// }
+static MinExp *transformMinMatch(MinMatch *node, Context *c) {
+ if (node == NULL)
+ return NULL;
+ ENTER(transformMinMatch);
+ MinExp *new_index = apply(c, node->index);
+ int save = PROTECT(new_index);
+ MinMatchList *new_cases = transformMinMatchList(node->cases, c);
+ PROTECT(new_cases);
+ MinExp *result = makeMinExp_Match(CPI(node), new_index, new_cases);
+ UNPROTECT(save);
+ LEAVE(transformMinMatch);
+ return result;
+}
+
+// cases |> identity && t(a)
+static MinMatchList *transformMinMatchList(MinMatchList *node, Context *c) {
+ if (node == NULL)
+ return NULL;
+ ENTER(transformMinMatchList);
+ MinExp *new_body = apply(c, node->body);
+ int save = PROTECT(new_body);
+ MinMatchList *new_next = transformMinMatchList(node->next, c);
+ PROTECT(new_next);
+ MinMatchList *result =
+ newMinMatchList(CPI(node), node->matches, new_body, new_next);
+ UNPROTECT(save);
+ LEAVE(transformMinMatchList);
+ return result;
+}
+
+// (M.letrec_expr(bindings, body)) {
+// M.letrec_expr(bindings |> identity && (t(a) && identity), t(a, body))
+// }
+static MinExp *transformMinLetRec(MinLetRec *node, Context *c) {
+ if (node == NULL)
+ return NULL;
+ ENTER(transformMinLetRec);
+ MinBindings *new_bindings = transformMinBindings(node->bindings, c);
+ int save = PROTECT(new_bindings);
+ MinExp *new_body = apply(c, node->body);
+ PROTECT(new_body);
+ MinExp *result = makeMinExp_LetRec(CPI(node), new_bindings, new_body);
+ UNPROTECT(save);
+ LEAVE(transformMinLetRec);
+ return result;
+}
+
+// identity && (t(a) && identity)
+static MinBindings *transformMinBindings(MinBindings *node, Context *c) {
+ if (node == NULL)
+ return NULL;
+ ENTER(transformMinBindings);
+ MinExp *new_val = apply(c, node->val);
+ int save = PROTECT(new_val);
+ MinBindings *new_next = transformMinBindings(node->next, c);
+ PROTECT(new_next);
+ MinBindings *result =
+ newMinBindings(CPI(node), node->var, new_val, new_next);
+ result->arity = node->arity;
+ UNPROTECT(save);
+ LEAVE(transformMinBindings);
+ return result;
+}
+
+// (M.amb_expr(e1, e2)) {
+// M.amb_expr(t(a, e1), t(a, e2))
+// }
+static MinExp *transformMinAmb(MinAmb *node, Context *c) {
+ if (node == NULL)
+ return NULL;
+ ENTER(transformMinAmb);
+ MinExp *new_left = apply(c, node->left);
+ int save = PROTECT(new_left);
+ MinExp *new_right = apply(c, node->right);
+ PROTECT(new_right);
+ MinExp *result = makeMinExp_Amb(CPI(node), new_left, new_right);
+ UNPROTECT(save);
+ LEAVE(transformMinAmb);
+ return result;
+}
+
+// (M.make_vec(size, elements)) {
+// M.make_vec(size, elements |> t(a))
+// }
+static MinExp *transformMinMakeVec(MinExprList *node, Context *c) {
+ if (node == NULL)
+ return NULL;
+ ENTER(transformMinMakeVec);
+ MinExprList *new_exprs = transformMinExprList(node, c);
+ int save = PROTECT(new_exprs);
+ MinExp *result = newMinExp_MakeVec(CPI(node), new_exprs);
+ UNPROTECT(save);
+ LEAVE(transformMinMakeVec);
+ return result;
+}
+
+// (M.sequence(exps)) {
+// M.sequence(exps |> t(a))
+// }
+static MinExp *transformMinSequence(MinExprList *node, Context *c) {
+ if (node == NULL)
+ return NULL;
+ ENTER(transformMinSequence);
+ MinExprList *new_exprs = transformMinExprList(node, c);
+ int save = PROTECT(new_exprs);
+ MinExp *result = newMinExp_Sequence(CPI(node), new_exprs);
+ UNPROTECT(save);
+ LEAVE(transformMinSequence);
+ return result;
+}
+
+static MinExp *transformMinExp(MinExp *node, Context *c) {
+ ENTER(transformMinExp);
+ if (node == NULL) {
+ LEAVE(transformMinExp);
+ return NULL;
+ }
+
+ int save = PROTECT(NULL);
+ MinExp *result = node;
+
+ switch (node->type) {
+ case MINEXP_TYPE_STDINT:
+ case MINEXP_TYPE_CHARACTER:
+ case MINEXP_TYPE_BACK:
+ case MINEXP_TYPE_BIGINTEGER:
+ case MINEXP_TYPE_VAR:
+ case MINEXP_TYPE_PRIM:
+ case MINEXP_TYPE_DONE:
+ break;
+
+ case MINEXP_TYPE_AMB:
+ result = transformMinAmb(getMinExp_Amb(node), c);
+ break;
+
+ case MINEXP_TYPE_APPLY:
+ result = transformMinApply(getMinExp_Apply(node), c);
+ break;
+
+ case MINEXP_TYPE_CALLCC:
+ result = transformMinExp(getMinExp_CallCC(node), c);
+ break;
+
+ case MINEXP_TYPE_COND:
+ result = transformMinCond(getMinExp_Cond(node), c);
+ break;
+
+ case MINEXP_TYPE_IFF:
+ result = transformMinIff(getMinExp_Iff(node), c);
+ break;
+
+ case MINEXP_TYPE_LAM:
+ result = transformMinLam(getMinExp_Lam(node), c);
+ break;
+
+ case MINEXP_TYPE_LETREC:
+ result = transformMinLetRec(getMinExp_LetRec(node), c);
+ break;
+
+ case MINEXP_TYPE_MAKEVEC:
+ result = transformMinMakeVec(getMinExp_MakeVec(node), c);
+ break;
+
+ case MINEXP_TYPE_MATCH:
+ result = transformMinMatch(getMinExp_Match(node), c);
+ break;
+
+ case MINEXP_TYPE_SEQUENCE:
+ result = transformMinSequence(getMinExp_Sequence(node), c);
+ break;
+
+ default:
+ cant_happen("unrecognized MinExp type %s", minExpTypeName(node->type));
+ }
+
+ UNPROTECT(save);
+ LEAVE(transformMinExp);
+ return result;
+}
diff --git a/src/minlam_transform.h b/src/minlam_transform.h
new file mode 100644
index 0000000..dc27cd5
--- /dev/null
+++ b/src/minlam_transform.h
@@ -0,0 +1,29 @@
+#ifndef cekf_minlam_transform_h
+#define cekf_minlam_transform_h
+/*
+ * CEKF - VM supporting amb
+ * Copyright (C) 2022-2026 Bill Hails
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "minlam.h"
+
+// MinExp -> Minexp
+typedef MinExp *(*MinExpFn)(MinExp *);
+
+MinExp *bottomUpMinExp(MinExpFn, MinExp *exp);
+MinExp *topDownMinExp(MinExpFn, MinExp *exp);
+
+#endif
diff --git a/src/utils.yaml b/src/utils.yaml
index 1bf43c7..f537532 100644
--- a/src/utils.yaml
+++ b/src/utils.yaml
@@ -120,4 +120,10 @@ vectors:
data:
entries: character
+ SymbolVec:
+ meta:
+ brief: A vector of Symbols
+ data:
+ entries: HashSymbol
+
primitives: !include primitives.yaml