gensym in Hy

Simple implementation of gensym in Hy.

Returns a new HySymbol.

Usable in macros like:

(defmacro nif [expr pos zero neg]
  (let [[g (gensym)]]
    `(let [[~g ~expr]]
       (cond [(pos? ~g) ~pos]
             [(zero? ~g) ~zero]
             [(neg? ~g) ~neg]))))

This addresses all the general comments about (gensym), and doesn't
try to implement "auto-gensym" yet. But clearly the macro approach
instead of the pre-processor approach (as described in the
letoverlambda (http://letoverlambda.com/index.cl/guest/chap3.html#sec_5)
is the way to go
This commit is contained in:
Bob Tolbert 2013-12-14 17:33:56 -07:00
parent c11b231c1c
commit f5d88bb108
4 changed files with 57 additions and 4 deletions

View File

@ -23,6 +23,7 @@
;;;; to make functional programming slightly easier.
;;;;
(import [hy._compat [long-type]]) ; long for python2, int for python3
(defn _numeric-check [x]
@ -91,6 +92,20 @@
"Return True if x is float"
(isinstance x float))
(import [threading [Lock]])
(setv _gensym_counter 1234)
(setv _gensym_lock (Lock))
(defn gensym [&optional [g "G"]]
(let [[new_symbol None]]
(global _gensym_counter)
(global _gensym_lock)
(.acquire _gensym_lock)
(try (do (setv _gensym_counter (inc _gensym_counter))
(setv new_symbol (HySymbol (.format ":{0}_{1}" g _gensym_counter))))
(finally (.release _gensym_lock)))
new_symbol))
(defn inc [n]
"Increment n by 1"
(_numeric-check n)
@ -223,6 +238,7 @@
(= n 0))
(def *exports* '[cycle dec distinct drop drop-while empty? even? filter float?
gensym
inc instance? integer integer? iterable? iterate iterator? neg?
none? nth numeric? odd? pos? remove repeat repeatedly second
string string? take take-nth take-while zero?])

View File

@ -113,6 +113,6 @@
(defmacro yield-from [iterable]
"Yield all the items from iterable"
;; TODO: this needs some gensym love
`(foreach [_hy_yield_from_x ~iterable]
(yield _hy_yield_from_x)))
(let [[x (gensym)]]
`(foreach [~x ~iterable]
(yield ~x))))

View File

@ -141,6 +141,18 @@
(assert-true (float? -3.2))
(assert-false (float? "foo")))
(defn test-gensym []
"NATIVE: testing the gensym function"
(import [hy.models.symbol [HySymbol]])
(setv s1 (gensym))
(assert (isinstance s1 HySymbol))
(assert (= 0 (.find s1 ":G_")))
(setv s2 (gensym "xx"))
(setv s3 (gensym "xx"))
(assert (= 0 (.find s2 ":xx_")))
(assert (not (= s2 s3)))
(assert (not (= (str s2) (str s3)))))
(defn test-inc []
"NATIVE: testing the inc function"
(assert-equal 3 (inc 2))
@ -393,3 +405,4 @@
(assert-equal res [None None])
(setv res (list (take-while (fn [x] (not (none? x))) [1 2 3 4 None 5 6 None 7])))
(assert-equal res [1 2 3 4]))

View File

@ -94,7 +94,6 @@
(assert initialized)
(assert (test-initialized))
(defn test-yield-from []
"NATIVE: testing yield from"
(defn yield-from-test []
@ -107,3 +106,28 @@
(import sys)
(assert (= (get sys.version_info 0)
(if-python2 2 3))))
(defn test-gensym-in-macros []
(import ast)
(import [astor.codegen [to_source]])
(import [hy.importer [import_buffer_to_ast]])
(setv macro1 "(defmacro nif [expr pos zero neg]
(let [[g (gensym)]]
`(let [[~g ~expr]]
(cond [(pos? ~g) ~pos]
[(zero? ~g) ~zero]
[(neg? ~g) ~neg]))))
(print (nif (inc -1) 1 0 -1))
")
;; expand the macro twice, should use a different
;; gensym each time
(setv _ast1 (import_buffer_to_ast macro1 "foo"))
(setv _ast2 (import_buffer_to_ast macro1 "foo"))
(setv s1 (to_source _ast1))
(setv s2 (to_source _ast2))
;; and make sure there is something new that starts with :G_
(assert (in ":G_" s1))
(assert (in ":G_" s2))
;; but make sure the two don't match each other
(assert (not (= s1 s2))))