Move logic from macroexpand_1 to macroexpand
By ending macro-expansion immediately when appropriate, this change fixes a bug arising from the fact that NaN != NaN.
This commit is contained in:
parent
026316ebef
commit
c1a487cdf7
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
|
||||
----------------------------
|
||||
|
85
hy/macros.py
85
hy/macros.py
@ -156,58 +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 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)
|
||||
return replace_hy_obj(obj, 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)
|
||||
|
Loading…
Reference in New Issue
Block a user