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