Just following the paper's convention (their justification being that now they only need to substitute variables for variables which is simpler), plus this gist is a trimmed down version of my hobby project which had full-blown algebraic data types: the integers were handled by that case as well. You absolutely can merge "int" case with the "var" case above, and use ints as themselves:
if isinstance(expr, str) or isinstance(expr, int):
result = CpsExprBuilder()
return maybe_tail(result, expr)
The second argument in maybe_tail would need a better name though...
> Why not support lambda expressions?
They were missing in your write-up as well :) I just went by your text, and look at the code snippets in it. But adding them looks like this:
if what == 'lambda':
args, body = rest
result = CpsExprBuilder()
tmp_name, ret = gen_tmp(), gen_cont()
fun = ['fun', [*args, ret], cps(body, ret)]
result.add_let_without_body([[tmp_name, fun]])
return maybe_tail(result, tmp_name)
Some test cases I've tried look like this then:
['lambda', ['x'], 'x'] =>
let
#t0 = fun(x, @k1):
$ret @k1(x)
in $ret @halt(#t0)
[['lambda', ['x'], 'x'], 2] =>
let
#t0 = fun(x, @k1):
$ret @k1(x)
#t2 = 2
in #t0(#t2, @halt)
['let', ['f', ['lambda', ['x'], 'x']], ['f', 2]] =>
let
@k0 = cont(f):
let
#t3 = 2
in f(#t3, @halt)
#t1 = fun(x, @k2):
$ret @k2(x)
in $ret @k0(#t1)
By the way, if you don't like creating contiunations just to immediately invoke them, you can adjust make_LET adjusted to flatten that pattern as well: check if the body is '$ret' with the continuation name that's in the defns list, replace body with this continuation's body (with variables substituted!) and throw its definition away.
[['lambda', ['x'], 'x'], 2] =>
let
#t0 = fun(x, @k1):
$ret @k1(x)
#t2 = 2
in #t0(#t2, @halt)
['let', ['f', ['lambda', ['x'], 'x']], ['f', 2]] =>
let
#t1 = fun(x, @k2):
$ret @k2(x)
#t3 = 2
in #t1(#t3, @halt)
But this also throws the programmer-supplied names away during the substitution, sadly. Preferring to keep programmer-supplied names is possible, of course, but it requires both tracking them and making sure they don't collide with temporaries or other programmer-supplied names so... this simplification step is better performed in the optimizer after the CPS-conversion.
* Why give literal ints their own temporaries? * Why not support lambda expressions? Is it tricky in this approach or just accidentally missing?