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:
Kodi Arfer 2018-04-09 12:01:12 -07:00
parent 026316ebef
commit c1a487cdf7
3 changed files with 54 additions and 43 deletions

View File

@ -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
---------------------------- ----------------------------

View File

@ -156,24 +156,16 @@ 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
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 == []: if not isinstance(tree, HyExpression) or tree == []:
return tree return tree
@ -207,7 +199,16 @@ 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)
return replace_hy_obj(obj, tree) 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
`compiler`."""
return macroexpand(tree, compiler, once=True)
def tag_macroexpand(tag, tree, compiler): def tag_macroexpand(tag, tree, compiler):

View File

@ -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)