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