bobbicodes / bobbi-lisp

Interactive Lisp environment for learning Clojure
https://bobbicodes.github.io/bobbi-lisp/
0 stars 0 forks source link

Functions as `recur` target (i.e. without `loop`) #9

Closed bobbicodes closed 11 months ago

bobbicodes commented 12 months ago

Currently, recur may only be used with loop. It took me an entire month to properly implement loop, so I got a bit burnt out. But in some ways, using a function as a recur point is actually easier, because we already have implicit tail recursion optimization unlike Clojure. Because of this fact, any use of recur without a loop I believe can be done by simply replacing the recur with the actual function! In fact, I had this working in a previous version. We have a postwalk function which I implemented specifically for this, which is currently not being used. How sad. So I know it works, at least for all the test cases I tried. All I needed to do was check if the function being defined has a loop, and if not, walk the AST and replace any occurrence of recur with the function itself (the function object, not its name, otherwise it wouldn't work with lambdas).

bobbicodes commented 11 months ago

The place where this was previously handled was in the function type:

function hasLoop(ast) {
    let loops = []
    postwalk(x => {
        if (x.value == _symbol("loop")) {
            loops.push(true)
            return true
        } else {
            return x
        }
        return x
    }, ast)
    if (loops.length > 0) {
        return true
    } else {
        return false
    }
}
export function _function(Eval, Env, ast, env, params) {
    var fn = function () {
        return Eval(ast, new Env(env, params, arguments))
    }
    let swapRecur = postwalk(x => {
        if (x.value == _symbol("recur")) {
            return fn
        } else {
            return x
        }
        return x
    }, ast)
    if (!hasLoop(ast)) {
        ast = swapRecur
        fn = function () {
            return Eval(ast, new Env(env, params, arguments))
        }
    }
    fn.__meta__ = null;
    fn.__ast__ = ast;
    fn.__gen_env__ = function (args) { return new Env(env, params, args); };
    fn._ismacro_ = false;
    return fn;
}

See https://github.com/bobbicodes/exercism-express/blob/8ebc9adb921858a28983cea4c0811a2cd40ed5d8/src/types.js#L208C1-L235C2

bobbicodes commented 11 months ago

Fixed by #22