Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Miscellany #2490

Merged
merged 4 commits into from
Aug 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Bug Fixes
* Double quotes inside of bracketed f-strings are now properly handled.
* Fixed incomplete recognition of macro calls with a unary dotted
head like `((. defn) f [])`.
* `~@ #*` now produces a syntax error instead of a nonsensical result.

0.27.0 (released 2023-07-06)
=============================
Expand Down
6 changes: 3 additions & 3 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,17 @@ base names, such that ``hy.core.macros.foo`` can be called as just ``foo``.

As a convenience, ``.`` supports two other kinds of arguments in place of a
plain attribute. A parenthesized expression is understood as a method call:
``(. foo (bar a b))`` compiles to ``x.foo.bar(a, b)``. A bracketed form is
``(. foo (bar a b))`` compiles to ``foo.bar(a, b)``. A bracketed form is
understood as a subscript: ``(. foo ["bar"])`` compiles to ``foo["bar"]``.
All these options can be mixed and matched in a single ``.`` call, so ::

(. a (b 1 2) c [d] [(e)])
(. a (b 1 2) c [d] [(e 3 4)])

compiles to

.. code-block:: python

a.b(1, 2).c[d][e()]
a.b(1, 2).c[d][e(3, 4)]

:ref:`Dotted identifiers <dotted-identifiers>` provide syntactic sugar for
common uses of this macro. In particular, syntax like ``foo.bar`` ends up
Expand Down
4 changes: 3 additions & 1 deletion docs/syntax.rst
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,9 @@ equivalent to ``{"a" 1 "b" 2}``, which is different from ``{:a 1 :b 2}`` (see
:ref:`dict-literals`).

The empty keyword ``:`` is syntactically legal, but you can't compile a
function call with an empty keyword argument.
function call with an empty keyword argument due to Python limitations. Thus
``(foo : 3)`` must be rewritten to use runtime unpacking, as in
``(foo #** {"" 3})``.

.. autoclass:: hy.models.Keyword
:members: __bool__, __call__
Expand Down
2 changes: 2 additions & 0 deletions hy/core/result_macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ def render_quoted_form(compiler, form, level):
for x in form:
f_contents, splice = render_quoted_form(compiler, x, level)
if splice:
if is_unpack("iterable", f_contents):
raise compiler._syntax_error(f_contents, "`unpack-iterable` is not allowed here")
f_contents = Expression(
[
Symbol("unpack-iterable"),
Expand Down
20 changes: 12 additions & 8 deletions hy/core/util.hy
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
(setv _gensym_counter 0)
(setv _gensym_lock (Lock))

(defn gensym [[g "G"]]
(defn gensym [[g ""]]
#[[Generate a symbol with a unique name. The argument will be included in the
generated symbol, as an aid to debugging. Typically one calls ``hy.gensym``
without an argument.
Expand All @@ -65,14 +65,18 @@
4)

(print (selfadd (f)))]]
(setv new_symbol None)
(global _gensym_counter)
(global _gensym_lock)
(.acquire _gensym_lock)
(try (do (setv _gensym_counter (+ _gensym_counter 1))
(setv new_symbol (Symbol (.format "_{}\uffff{}" g _gensym_counter))))
(finally (.release _gensym_lock)))
new_symbol)
(try
(global _gensym_counter)
(+= _gensym_counter 1)
(setv n _gensym_counter)
(finally (.release _gensym_lock)))
(setv g (hy.mangle (.format "_hy_gensym_{}_{}" g n)))
(Symbol (if (.startswith g "_hyx_")
; Remove the mangle prefix, if it's there, so the result always
; starts with our reserved prefix `_hy_`.
(+ "_" (cut g (len "_hyx_") None))
g)))

(defn _calling-module-name [[n 1]]
"Get the name of the module calling `n` levels up the stack from the
Expand Down
6 changes: 3 additions & 3 deletions hy/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ class Symbol(Object, str):
"""
Represents a symbol.

Symbol objects behave like strings under operations like :hy:func:`get`,
Symbol objects behave like strings under operations like :hy:func:`get <hy.pyops.get>`,
:func:`len`, and :class:`bool`; in particular, ``(bool (hy.models.Symbol "False"))`` is true. Use :hy:func:`hy.eval` to evaluate a symbol.
"""

Expand Down Expand Up @@ -283,8 +283,8 @@ def __call__(self, data, default=_sentinel):
:class:`hy.models.Keyword` objects).

The optional second parameter is a default value; if provided, any
:class:`KeyError` from :hy:func:`get` will be caught, and the default returned
instead."""
:class:`KeyError` from :hy:func:`get <hy.pyops.get>` will be caught,
and the default returned instead."""

from hy.reader import mangle

Expand Down
9 changes: 5 additions & 4 deletions tests/native_tests/hy_misc.hy
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
(defn test-gensym []
(setv s1 (hy.gensym))
(assert (isinstance s1 hy.models.Symbol))
(assert (= 0 (.find s1 "_G\uffff")))
(assert (.startswith s1 "_hy_gensym__"))
(setv s2 (hy.gensym "xx"))
(setv s3 (hy.gensym "xx"))
(assert (= 0 (.find s2 "_xx\uffff")))
(assert (not (= s2 s3)))
(assert (not (= (str s2) (str s3)))))
(assert (.startswith s2 "_hy_gensym_xx_"))
(assert (!= s2 s3))
(assert (!= (str s2) (str s3)))
(assert (.startswith (hy.gensym "•ab") "_hy_gensym_XbulletXab_")))


(defmacro mac [x expr]
Expand Down
35 changes: 11 additions & 24 deletions tests/native_tests/macros.hy
Original file line number Diff line number Diff line change
Expand Up @@ -120,31 +120,18 @@
(assert initialized)
(assert (test-initialized))


(defmacro gensym-example []
`(setv ~(hy.gensym) 1))

(defn test-gensym-in-macros []
(import ast)
(import hy.compiler [hy-compile])
(import hy.reader [read-many])
(setv macro1 "(defmacro nif [expr pos zero neg]
(setv g (hy.gensym))
`(do
(setv ~g ~expr)
(cond (> ~g 0) ~pos
(= ~g 0) ~zero
(< ~g 0) ~neg)))

(print (nif (inc -1) 1 0 -1))
")
;; expand the macro twice, should use a different
;; gensym each time
(setv _ast1 (hy-compile (read-many macro1) __name__))
(setv _ast2 (hy-compile (read-many macro1) __name__))
(setv s1 (ast.unparse _ast1))
(setv s2 (ast.unparse _ast2))
;; and make sure there is something new that starts with _G\uffff
(assert (in (hy.mangle "_G\uffff") s1))
(assert (in (hy.mangle "_G\uffff") s2))
;; but make sure the two don't match each other
(assert (not (= s1 s2))))
; Call `gensym-example` twice, getting a distinct gensym each time.
(defclass C []
(gensym-example)
(gensym-example))
(assert (=
(len (sfor a (dir C) :if (not (.startswith a "__")) a))
2)))


(defn test-macro-errors []
Expand Down
10 changes: 10 additions & 0 deletions tests/native_tests/quote.hy
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
(import
pytest)


(defn test-quote []
(setv q (quote (a b c)))
(assert (= (len q) 3))
Expand Down Expand Up @@ -83,3 +87,9 @@
[1 2 3]
[4 5 6])
[4 5 6])))


(defn test-unquote-splice-unpack []
; https://github.com/hylang/hy/issues/2336
(with [(pytest.raises hy.errors.HySyntaxError)]
(hy.eval '`[~@ #* [[1]]])))