Merge pull request #1575 from Kodiologist/fix-macroexpand-nan
Fix bug in macro-expanding NaN
This commit is contained in:
commit
e3d21118c0
1
NEWS.rst
1
NEWS.rst
@ -42,6 +42,7 @@ Bug Fixes
|
||||
* Fixed a case where `->` and `->>` duplicated an argument
|
||||
* Fixed bugs that caused `defclass` to drop statements or crash
|
||||
* Fixed a REPL crash caused by illegle unicode escape string inputs
|
||||
* `NaN` can no longer create an infinite loop during macro-expansion
|
||||
|
||||
Misc. Improvements
|
||||
----------------------------
|
||||
|
@ -423,6 +423,8 @@ class HyASTCompiler(object):
|
||||
# pass in `atom_type`.
|
||||
atom_compiler = _compile_table[atom_type]
|
||||
arity = hy.inspect.get_arity(atom_compiler)
|
||||
# Compliation methods may mutate the atom, so copy it first.
|
||||
atom = copy.copy(atom)
|
||||
ret = (atom_compiler(self, atom, atom_type)
|
||||
if arity == 3
|
||||
else atom_compiler(self, atom))
|
||||
|
@ -41,6 +41,7 @@
|
||||
(if ~@(cut args 2))))))
|
||||
|
||||
(defmacro deftag [tag-name lambda-list &rest body]
|
||||
(import hy.models)
|
||||
(if (and (not (isinstance tag-name hy.models.HySymbol))
|
||||
(not (isinstance tag-name hy.models.HyString)))
|
||||
(raise (hy.errors.HyTypeError
|
||||
|
@ -297,16 +297,12 @@ Return series of accumulated sums (or other binary function results)."
|
||||
(defn macroexpand [form]
|
||||
"Return the full macro expansion of `form`."
|
||||
(import hy.macros)
|
||||
|
||||
(setv name (calling-module-name))
|
||||
(hy.macros.macroexpand form (HyASTCompiler name)))
|
||||
(hy.macros.macroexpand form (HyASTCompiler (calling-module-name))))
|
||||
|
||||
(defn macroexpand-1 [form]
|
||||
"Return the single step macro expansion of `form`."
|
||||
(import hy.macros)
|
||||
|
||||
(setv name (calling-module-name))
|
||||
(hy.macros.macroexpand-1 form (HyASTCompiler name)))
|
||||
(hy.macros.macroexpand-1 form (HyASTCompiler (calling-module-name))))
|
||||
|
||||
(defn merge-with [f &rest maps]
|
||||
"Return the map of `maps` joined onto the first via the function `f`.
|
||||
|
93
hy/macros.py
93
hy/macros.py
@ -156,66 +156,59 @@ def make_empty_fn_copy(fn):
|
||||
return empty_fn
|
||||
|
||||
|
||||
def macroexpand(tree, compiler):
|
||||
def macroexpand(tree, compiler, once=False):
|
||||
"""Expand the toplevel macros for the `tree`.
|
||||
|
||||
Load the macros from the given `module_name`, then expand the (top-level)
|
||||
macros in `tree` until it stops changing.
|
||||
macros in `tree` until we no longer can.
|
||||
|
||||
"""
|
||||
load_macros(compiler.module_name)
|
||||
old = None
|
||||
while old != tree:
|
||||
old = tree
|
||||
tree = macroexpand_1(tree, compiler)
|
||||
return tree
|
||||
while True:
|
||||
|
||||
if not isinstance(tree, HyExpression) or tree == []:
|
||||
return tree
|
||||
|
||||
fn = tree[0]
|
||||
if fn in ("quote", "quasiquote") or not isinstance(fn, HySymbol):
|
||||
return tree
|
||||
|
||||
fn = mangle(fn)
|
||||
m = _hy_macros[compiler.module_name].get(fn) or _hy_macros[None].get(fn)
|
||||
if not m:
|
||||
return tree
|
||||
|
||||
opts = {}
|
||||
if m._hy_macro_pass_compiler:
|
||||
opts['compiler'] = compiler
|
||||
|
||||
try:
|
||||
m_copy = make_empty_fn_copy(m)
|
||||
m_copy(compiler.module_name, *tree[1:], **opts)
|
||||
except TypeError as e:
|
||||
msg = "expanding `" + str(tree[0]) + "': "
|
||||
msg += str(e).replace("<lambda>()", "", 1).strip()
|
||||
raise HyMacroExpansionError(tree, msg)
|
||||
|
||||
try:
|
||||
obj = m(compiler.module_name, *tree[1:], **opts)
|
||||
except HyTypeError as e:
|
||||
if e.expression is None:
|
||||
e.expression = tree
|
||||
raise
|
||||
except Exception as e:
|
||||
msg = "expanding `" + str(tree[0]) + "': " + repr(e)
|
||||
raise HyMacroExpansionError(tree, msg)
|
||||
tree = replace_hy_obj(obj, tree)
|
||||
|
||||
if once:
|
||||
return tree
|
||||
|
||||
|
||||
def macroexpand_1(tree, compiler):
|
||||
"""Expand the toplevel macro from `tree` once, in the context of
|
||||
`module_name`."""
|
||||
if isinstance(tree, HyExpression):
|
||||
if tree == []:
|
||||
return tree
|
||||
|
||||
fn = tree[0]
|
||||
if fn in ("quote", "quasiquote"):
|
||||
return tree
|
||||
ntree = HyExpression(tree[:])
|
||||
ntree.replace(tree)
|
||||
|
||||
opts = {}
|
||||
|
||||
if isinstance(fn, HySymbol):
|
||||
fn = mangle(str_type(fn))
|
||||
m = _hy_macros[compiler.module_name].get(fn)
|
||||
if m is None:
|
||||
m = _hy_macros[None].get(fn)
|
||||
if m is not None:
|
||||
if m._hy_macro_pass_compiler:
|
||||
opts['compiler'] = compiler
|
||||
|
||||
try:
|
||||
m_copy = make_empty_fn_copy(m)
|
||||
m_copy(compiler.module_name, *ntree[1:], **opts)
|
||||
except TypeError as e:
|
||||
msg = "expanding `" + str(tree[0]) + "': "
|
||||
msg += str(e).replace("<lambda>()", "", 1).strip()
|
||||
raise HyMacroExpansionError(tree, msg)
|
||||
|
||||
try:
|
||||
obj = m(compiler.module_name, *ntree[1:], **opts)
|
||||
except HyTypeError as e:
|
||||
if e.expression is None:
|
||||
e.expression = tree
|
||||
raise
|
||||
except Exception as e:
|
||||
msg = "expanding `" + str(tree[0]) + "': " + repr(e)
|
||||
raise HyMacroExpansionError(tree, msg)
|
||||
replace_hy_obj(obj, tree)
|
||||
return obj
|
||||
return ntree
|
||||
return tree
|
||||
`compiler`."""
|
||||
return macroexpand(tree, compiler, once=True)
|
||||
|
||||
|
||||
def tag_macroexpand(tag, tree, compiler):
|
||||
|
@ -5,7 +5,7 @@
|
||||
from hy.macros import macro, macroexpand
|
||||
from hy.lex import tokenize
|
||||
|
||||
from hy.models import HyString, HyList, HySymbol, HyExpression
|
||||
from hy.models import HyString, HyList, HySymbol, HyExpression, HyFloat
|
||||
from hy.errors import HyMacroExpansionError
|
||||
|
||||
from hy.compiler import HyASTCompiler
|
||||
@ -53,3 +53,12 @@ def test_preprocessor_exceptions():
|
||||
except HyMacroExpansionError as e:
|
||||
assert "_hy_anon_fn_" not in str(e)
|
||||
assert "TypeError" not in str(e)
|
||||
|
||||
|
||||
def test_macroexpand_nan():
|
||||
# https://github.com/hylang/hy/issues/1574
|
||||
import math
|
||||
NaN = float('nan')
|
||||
x = macroexpand(HyFloat(NaN), HyASTCompiler(__name__))
|
||||
assert type(x) is HyFloat
|
||||
assert math.isnan(x)
|
||||
|
@ -2,7 +2,7 @@
|
||||
;; This file is part of Hy, which is free software licensed under the Expat
|
||||
;; license. See the LICENSE.
|
||||
|
||||
(import [hy [HyExpression HySymbol HyString HyBytes]])
|
||||
(import [hy [HyExpression HySymbol HyString HyBytes HyDict]])
|
||||
|
||||
|
||||
(defn test-quote []
|
||||
@ -43,7 +43,7 @@
|
||||
(assert (= (get q 1) (quote bar)))
|
||||
(assert (= (get q 2) (quote baz)))
|
||||
(assert (= (get q 3) (quote quux)))
|
||||
(assert (= (type q) hy.HyDict)))
|
||||
(assert (= (type q) HyDict)))
|
||||
|
||||
|
||||
(defn test-quote-expr-in-dict []
|
||||
|
Loading…
Reference in New Issue
Block a user