Permalink
Browse files

Merge branch 'auto-recur'

  • Loading branch information...
2 parents e96aecd + 17cd20d commit 18074cfadb8dadbf3479f686f9cf284b18f5e25e @cjfrisz committed Nov 5, 2012
View
@@ -143,11 +143,37 @@ following code:
([n a k4583]
(if (zero? n)
(k4583 a)
- (with-meta
- #(fact (dec n) (* n a) k4583)
- {:thunk true})))))))
+ (recur (dec n) (* n a) k4583)))))))
```
+## Bonus: 'recurify' macro
+
+CTCO also provides a `recurify` macro which takes any expression
+accepted by the CTCO grammar and replaces all self-recursive tail
+calls that explicitly use a function name to instead use the `recur`
+form. It simply leverages the mechanism for doing the same
+transformation within the full CTCO transformation.
+
+For example:
+
+```clojure
+(recurify
+ (defn fact
+ [n a]
+ (if (zero? n)
+ a
+ (fact (dec n) (* n a)))))
+```
+
+This will expand to the following:
+
+```clojure
+(defn fact
+ [n a]
+ (if (zero? n)
+ a
+ (recur (dec n) (* n a))))
+```
## Contributing
View
@@ -3,12 +3,12 @@
;; Written by Chris Frisz
;;
;; Created 4 Feb 2012
-;; Last modified 6 Oct 2012
+;; Last modified 5 Nov 2012
;;
;; Project declaration for clojure-tco.
;;----------------------------------------------------------------------
-(defproject ctco "0.4.2"
+(defproject chrisfrisz.com/ctco "0.5.0"
:description "Improving Clojure's support for constant-space tail calls."
:url "https://github.com/cjfrisz/clojure-tco"
:license {:name "The MIT License"
View
@@ -3,15 +3,15 @@
;; Written by Chris Frisz
;;
;; Created 11 Apr 2012
-;; Last modified 6 Oct 2012
+;; Last modified 5 Nov 2012
;;
;; Defines the ctco macro which acts as the driver for the Clojure TCO
;; compiler. The macro parses the initial expression, and applies the
;; main Clojure TCO transformations to the expression, including the CPS
;; transformation, thunkification, and trampolining. Finally, the code
;; is unparsed and wrapped in a binding for the custom trampoline
;; function and an initial call to that trampoline function.
-;; ----------------------------------------------------------------------
+;;----------------------------------------------------------------------
(ns ctco.core
(:require [ctco.parse :as parse]
@@ -37,7 +37,9 @@
proto/PCpsSrs (proto/cps-srs expr (gensym "k"))
:else (throw (Exception. (str "unexpected expression " expr)))))]
(let [new-expr (-> (parse/parse expr)
+ (proto/unrecurify nil)
apply-cps
+ (proto/recurify nil nil false)
proto/thunkify
(proto/load-tramp tramp)
proto/unparse)]
@@ -47,3 +49,12 @@
(recur (~thunk))
~thunk))]
(~tramp ~new-expr)))))))
+
+(defmacro recurify
+ [expr]
+ "A macro which replaces self-recursive function calls explicitly using the
+ function's name with uses of the 'recur' form for using constant stack space
+ for self-recursive function calls."
+ (-> (parse/parse expr)
+ (proto/recurify nil nil false)
+ proto/unparse))
View
@@ -3,7 +3,7 @@
;; Written by Chris Frisz
;;
;; Created 2 Apr 2012
-;; Last modified 6 Oct 2012
+;; Last modified 20 Oct 2012
;;
;; Defines the App record type for function application in the Clojure
;; TCO compiler.
@@ -23,6 +23,17 @@
;; Recursively applies load-tramp to the operator and
;; operands of the expression.
;;
+;; PRecurify:
+;; If the name argument matches the rator and the value of
+;; tail? is true, replaces the application with the recur
+;; form. If not, returns a new application with recurify
+;; applied to the rator with nil and false for the values
+;; of name and tail?, respectively, since the rator cannot
+;; be a tail call. Regardless of whether the application is
+;; a self-recursive tail call, recurify is applied to the
+;; arguments with values nil and false for name and tail?,
+;; respectively.
+;;
;; PThunkify:
;; Recursively thunkifies the rator and each rand* and
;; puts the result inside of a thunk.
@@ -31,18 +42,25 @@
;; Unparses (recursively) the sytax for the expression as
;; `(~rator ~@rand*)
;;
+;; PUnRecurify
+;; Maps the unrecurify transformation over the rator and
+;; rand* of the application. Uses the walk-expr function
+;; provided by PWalkable.
+;;
;; PWalkable:
;; Applies the given function to the rator and each rand*
;; and returns a new App record.
;;----------------------------------------------------------------------
(ns ctco.expr.app
(:require [ctco.expr
- cont thunk]
+ cont simple thunk]
[ctco.protocol :as proto]
[ctco.util :as util])
(:import [ctco.expr.cont
Cont AppCont]
+ [ctco.expr.simple
+ Simple]
[ctco.expr.thunk
Thunk]))
@@ -68,15 +86,41 @@
proto/PLoadTrampoline
(load-tramp [this tramp]
(proto/walk-expr this #(proto/load-tramp % tramp) nil))
+
+ proto/PRecurify
+ (recurify [this name arity tail?]
+ (let [rator (:rator this)
+ rand* (:rand* this)
+ RAND* (mapv #(proto/recurify % nil nil false) rand*)]
+ (if (and (= rator name) (= (count rand*) arity))
+;; NB: seems like a manifest constant
+;; NB: maybe fix this with the globals file
+ (App. (Simple. 'recur) RAND*)
+ (App. (proto/recurify rator nil nil false) RAND*))))
proto/PThunkify
(thunkify [this]
- (Thunk. (proto/walk-expr this proto/thunkify nil)))
+;; NB: seems like a manifest constant
+;; NB: maybe fix this with the globals file
+ (let [APP (proto/walk-expr this proto/thunkify nil)]
+ (if (= (:rator this) (Simple. 'recur))
+ APP
+ (Thunk. APP))))
proto/PUnparse
(unparse [this]
`(~(proto/unparse (:rator this)) ~@(map proto/unparse (:rand* this))))
+ proto/PUnRecurify
+ (unrecurify [this name]
+;; NB: seems like a manifest constant
+;; NB: maybe fix this with the globals file
+ (let [recur-rec (Simple. 'recur)
+ unrecurify #(proto/unrecurify % name)]
+ (if (= (:rator this) recur-rec)
+ (App. recur-rec (mapv unrecurify (:rand* this)))
+ (proto/walk-expr this unrecurify nil))))
+
proto/PWalkable
(walk-expr [this f _]
(App. (f (:rator this)) (mapv f (:rand* this)))))
@@ -3,7 +3,7 @@
;; Written by Chris Frisz
;;
;; Created 1 Apr 2012
-;; Last modified 13 Oct 2012
+;; Last modified 5 Nov 2012
;;
;; Defines the Cont, AppCont, and AppContAbs record types for
;; continuations, continuation application, and continuation
@@ -38,6 +38,14 @@
;; Applies the load-tramp function to each subexpression.
;; Uses the walk-expr function provided by PWalkable.
;;
+;; PRecurify:
+;; Applies the recurify function to each
+;; subexpression. Since neither a continuation nor a
+;; continuation application is a tail call, 'false' is
+;; passed for the 'tail?' value and 'nil' for the name in
+;; the recursive calls. Uses the walk-exp function
+;; provided by PWalkable.
+;;
;; PThunkify:
;; Applies the thunkify function to each subexpression.
;; Uses the walk-expr function provided by PWalkable.
@@ -72,6 +80,10 @@
(load-tramp [this tramp]
(proto/walk-expr this #(proto/load-tramp % tramp) nil))
+ proto/PRecurify
+ (recurify [this name arity tail?]
+ (proto/walk-expr this #(proto/recurify % nil nil false) nil))
+
proto/PThunkify
(thunkify [this]
(proto/walk-expr this proto/thunkify nil)))
View
@@ -3,7 +3,7 @@
;; Written by Chris
;;
;; Created 30 Aug 2012
-;; Last modified 6 Oct 2012
+;; Last modified 20 Oct 2012
;;
;; Defines the DefSrs, DefTriv, and DefCps record types for representing
;; 'def' expression in the Clojure TCO compiler.
@@ -16,6 +16,14 @@
;; the same symbol name. Uses the walk-expr function
;; provided by PWalkable.
;;
+;; PRecurify:
+;; Applies recurify to the init expression, generating a
+;; new DefCps with the same symbol name. Since a 'def'
+;; expression is not considered a tail call, uses 'false'
+;; for the 'tail?' value in the recursive calls and 'nil'
+;; for name. Uses the walk-expr function provided by
+;; PWalkable.
+;;
;; PThunkify:
;; Applies thunkify to the init expression, generating a
;; new DefCps with the same symbol name. Uses the walk-expr
@@ -28,6 +36,14 @@
;; DefCps with the same symbol name. Uses the walk-expr
;; function provided by PWalkable.
;;
+;; PRecurify:
+;; Applies recurify to the init expression, generating a
+;; new DefSrs with the same symbol name. Since a 'def'
+;; expression is not considered a tail call, uses 'false'
+;; for the 'tail?' value in the recursive calls and 'nil'
+;; for name. Uses the walk-expr function provided by
+;; PWalkable.
+;;
;; PLoadTrampoline:
;; Applies load-tramp to the init expression for the given
;; trampoline function name, generating a new DefSrs with
@@ -41,6 +57,14 @@
;; DefCps with the same symbol name. Uses the walk-expr
;; function provided by PWalkable.
;;
+;; PRecurify:
+;; Applies recurify to the init expression, generating a
+;; new DefTriv with the same symbol name. Since a 'def'
+;; expression is not considered a tail call, uses 'false'
+;; for the 'tail?' value in the recursive calls and 'nil'
+;; for name. Uses the walk-expr function provided by
+;; PWalkable.
+;;
;; PLoadTrampoline:
;; Applies load-tramp to the init expression for the given
;; trampoline function name, generating a new DefTriv with
@@ -69,6 +93,10 @@
proto/PLoadTrampoline
(load-tramp [this tramp]
(proto/walk-expr this #(proto/load-tramp % tramp) #(DefCps. %1 %2)))
+
+ proto/PRecurify
+ (recurify [this name arity tail?]
+ (proto/walk-expr this #(proto/recurify % nil nil false) #(DefCps. %1 %2)))
proto/PThunkify
(thunkify [this]
@@ -79,18 +107,34 @@
(cps-srs [this k]
(proto/walk-expr this #(proto/cps-srs % k) #(DefCps. %1 %2)))
+ proto/PRecurify
+ (recurify [this name arity tail?]
+ (proto/walk-expr this #(proto/recurify % nil nil false) #(DefSrs. %1 %2)))
+
proto/PLoadTrampoline
(load-tramp [this tramp]
- (proto/walk-expr this #(proto/load-tramp % tramp) #(DefSrs. %1 %2))))
+ (proto/walk-expr this #(proto/load-tramp % tramp) #(DefSrs. %1 %2)))
+
+ proto/PUnRecurify
+ (unrecurify [this name]
+ (proto/walk-expr this #(proto/unrecurify % name) #(DefSrs. %1 %2))))
(defrecord DefTriv [sym init]
proto/PCpsTriv
(cps-triv [this]
(proto/walk-expr this proto/cps-triv #(DefCps. %1 %2)))
+ proto/PRecurify
+ (recurify [this name arity tail?]
+ (proto/walk-expr this #(proto/recurify % nil nil false) #(DefTriv. %1 %2)))
+
proto/PLoadTrampoline
(load-tramp [this tramp]
- (proto/walk-expr this #(proto/load-tramp % tramp) #(DefTriv. %1 %2))))
+ (proto/walk-expr this #(proto/load-tramp % tramp) #(DefTriv. %1 %2)))
+
+ proto/PUnRecurify
+ (unrecurify [this name]
+ (proto/walk-expr this #(proto/unrecurify % name) #(DefTriv. %1 %2))))
(util/extend-multi (DefCps DefSrs DefTriv)
proto/PUnparse
View
@@ -3,7 +3,7 @@
;; Written by Chris Frisz
;;
;; Created 16 Apr 2012
-;; Last modified 6 Oct 2012
+;; Last modified 5 Nov 2012
;;
;; Defines the Do record type and operations for 'do' expressions in the
;; Clojure TCO compiler.
@@ -23,6 +23,10 @@
;; Unparses (recursively) the syntax for the expression as
;; `(do ~@expr*).
;;
+;; PUnRecurify:
+;; Applies unrecurify to each expression. Uses the
+;; walk-expr function provided by PWalkable.
+;;
;; PWalkable:
;; Applies the given function to each expression, creating
;; a new Do record with the results.
@@ -36,11 +40,21 @@
(load-tramp [this tramp]
(proto/walk-expr this #(proto/load-tramp % tramp) nil))
+ proto/PRecurify
+ (recurify [this name arity tail?]
+ (let [expr* (:expr* this)]
+ (Do. (conj (mapv #(proto/recurify % nil nil false) (butlast expr*))
+ (proto/recurify (last expr*) name arity tail?)))))
+
proto/PThunkify
(thunkify [this] (proto/walk-expr this proto/thunkify nil))
proto/PUnparse
(unparse [this] `(do ~@(map proto/unparse (:expr* this))))
+ proto/PUnRecurify
+ (unrecurify [this name]
+ (proto/walk-expr this #(proto/unrecurify % name) nil))
+
proto/PWalkable
(walk-expr [this f _] (Do. (mapv f (:expr* this)))))
Oops, something went wrong.

0 comments on commit 18074cf

Please sign in to comment.