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. ;;;; to make functional programming slightly easier.
;;;; ;;;;
(import [hy._compat [long-type]]) ; long for python2, int for python3 (import [hy._compat [long-type]]) ; long for python2, int for python3
(defn _numeric-check [x] (defn _numeric-check [x]
@ -91,6 +92,20 @@
"Return True if x is float" "Return True if x is float"
(isinstance x 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] (defn inc [n]
"Increment n by 1" "Increment n by 1"
(_numeric-check n) (_numeric-check n)
@ -223,6 +238,7 @@
(= n 0)) (= n 0))
(def *exports* '[cycle dec distinct drop drop-while empty? even? filter float? (def *exports* '[cycle dec distinct drop drop-while empty? even? filter float?
gensym
inc instance? integer integer? iterable? iterate iterator? neg? inc instance? integer integer? iterable? iterate iterator? neg?
none? nth numeric? odd? pos? remove repeat repeatedly second none? nth numeric? odd? pos? remove repeat repeatedly second
string string? take take-nth take-while zero?]) string string? take take-nth take-while zero?])

View File

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

View File

@ -141,6 +141,18 @@
(assert-true (float? -3.2)) (assert-true (float? -3.2))
(assert-false (float? "foo"))) (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 [] (defn test-inc []
"NATIVE: testing the inc function" "NATIVE: testing the inc function"
(assert-equal 3 (inc 2)) (assert-equal 3 (inc 2))
@ -393,3 +405,4 @@
(assert-equal res [None None]) (assert-equal res [None None])
(setv res (list (take-while (fn [x] (not (none? x))) [1 2 3 4 None 5 6 None 7]))) (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])) (assert-equal res [1 2 3 4]))

View File

@ -94,7 +94,6 @@
(assert initialized) (assert initialized)
(assert (test-initialized)) (assert (test-initialized))
(defn test-yield-from [] (defn test-yield-from []
"NATIVE: testing yield from" "NATIVE: testing yield from"
(defn yield-from-test [] (defn yield-from-test []
@ -107,3 +106,28 @@
(import sys) (import sys)
(assert (= (get sys.version_info 0) (assert (= (get sys.version_info 0)
(if-python2 2 3)))) (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))))