Skip to content

Instantly share code, notes, and snippets.

@simenge
Created March 17, 2019 02:42
Show Gist options
  • Save simenge/2d8e0f60b3df3f4e8503319f58eb5c79 to your computer and use it in GitHub Desktop.
Save simenge/2d8e0f60b3df3f4e8503319f58eb5c79 to your computer and use it in GitHub Desktop.
def annotate_tc(s : LispObject)
# Automatically annotating tail calls can be tricky, because we need to
# properly recurse while terminating at the right points. In particular:
# * The last expr in a function body is in tail position (TP)
# * If a function call is in TP, we need to annotate it
# * If an if expr is in TP, we need to annotate each branch
# * If a fn literal is in TP, we need to recursively call annotate_tc.
# However, we will not do this from within annotate_tc; rather,
# Interpreter#eval_fn, which evaluates a function literal and
# returns a Lisp::Lambda object, will call
# annotate_tc once it evaluates the inner function
# * Otherwise, we need to return the value unchanged
#
# This function receives only the function body, and returns another
# properly annotated function body.
return s unless s.is_a?(List)
s = s.as(List)
last = s[-1]
return s unless last.is_a?(List)
return s if last.empty?
last = last.as List
if last.first == sym("if")
s[-1] = annotate_tc_if last
return s
elsif funcall?(last)
s[-1].as(List).unshift sym("tailcall")
return s
else
return s
end
end
def annotate_tc_if(s : List) : LispObject
if s.size < 3
error("if must have at least 2 args")
end
_then = s[2]
if funcall?(_then)
s[2].as(List).unshift sym("tailcall").as(Lisp::Symbol)
else
s[2] = annotate_tc s[2]
end
if s.size == 4
if funcall? s[3]
s[3].as(List).unshift sym("tailcall").as(Lisp::Symbol)
else
s[3] = annotate_tc s[3]
end
end
s
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment