Require capitalizing NaN and Inf like so

This commit is contained in:
Kodi Arfer 2017-05-14 11:12:28 -04:00
parent a746ccb42c
commit bb91b57dca
4 changed files with 51 additions and 6 deletions

1
NEWS
View File

@ -8,6 +8,7 @@ Changes from 0.13.0
* The compiler now automatically promotes values to Hy model objects
as necessary, so you can write ``(eval `(+ 1 ~n))`` instead of
``(eval `(+ 1 ~(HyInteger n)))``
* Literal `Inf`s and `NaN`s must now be capitalized like that
[ Bug Fixes ]
* Numeric literals are no longer parsed as symbols when followed by a dot

View File

@ -52,6 +52,9 @@ digits.
(print 10,000,000,000 10_000_000_000)
Unlike Python, Hy provides literal forms for NaN and infinity: `NaN`, `Inf`,
and `-Inf`.
string literals
---------------

View File

@ -3,6 +3,7 @@
# license. See the LICENSE.
from __future__ import unicode_literals
from math import isnan, isinf
from hy._compat import PY3, str_type, bytes_type, long_type, string_types
from fractions import Fraction
@ -142,15 +143,24 @@ if not PY3: # do not add long on python3
_wrappers[long_type] = HyInteger
def check_inf_nan_cap(arg, value):
if isinstance(arg, string_types):
if isinf(value) and "Inf" not in arg:
raise ValueError('Inf must be capitalized as "Inf"')
if isnan(value) and "NaN" not in arg:
raise ValueError('NaN must be capitalized as "NaN"')
class HyFloat(HyObject, float):
"""
Internal representation of a Hy Float. May raise a ValueError as if
float(foo) was called, given HyFloat(foo).
"""
def __new__(cls, number, *args, **kwargs):
number = float(strip_digit_separators(number))
return super(HyFloat, cls).__new__(cls, number)
def __new__(cls, num, *args, **kwargs):
value = super(HyFloat, cls).__new__(cls, strip_digit_separators(num))
check_inf_nan_cap(num, value)
return value
_wrappers[float] = HyFloat
@ -161,9 +171,18 @@ class HyComplex(HyObject, complex):
complex(foo) was called, given HyComplex(foo).
"""
def __new__(cls, number, *args, **kwargs):
number = complex(strip_digit_separators(number))
return super(HyComplex, cls).__new__(cls, number)
def __new__(cls, num, *args, **kwargs):
value = super(HyComplex, cls).__new__(cls, strip_digit_separators(num))
if isinstance(num, string_types):
p1, _, p2 = num.lstrip("+-").replace("-", "+").partition("+")
if p2:
check_inf_nan_cap(p1, value.real)
check_inf_nan_cap(p2, value.imag)
elif "j" in p1:
check_inf_nan_cap(p1, value.imag)
else:
check_inf_nan_cap(p1, value.real)
return value
_wrappers[complex] = HyComplex

View File

@ -2,6 +2,7 @@
# This file is part of Hy, which is free software licensed under the Expat
# license. See the LICENSE.
from math import isnan
from hy.models import (HyExpression, HyInteger, HyFloat, HyComplex, HySymbol,
HyString, HyDict, HyList, HySet, HyCons)
from hy.lex import LexException, PrematureEndOfInput, tokenize
@ -91,6 +92,22 @@ def test_lex_expression_float():
assert objs == [HyExpression([HySymbol("foo"), HyFloat(1.e7)])]
def test_lex_nan_and_inf():
assert isnan(tokenize("NaN")[0])
assert tokenize("Nan") == [HySymbol("Nan")]
assert tokenize("nan") == [HySymbol("nan")]
assert tokenize("NAN") == [HySymbol("NAN")]
assert tokenize("Inf") == [HyFloat(float("inf"))]
assert tokenize("inf") == [HySymbol("inf")]
assert tokenize("INF") == [HySymbol("INF")]
assert tokenize("-Inf") == [HyFloat(float("-inf"))]
assert tokenize("-inf") == [HySymbol("_inf")]
assert tokenize("-INF") == [HySymbol("_INF")]
def test_lex_expression_complex():
""" Make sure expressions can produce complex """
@ -102,6 +119,11 @@ def test_lex_expression_complex():
assert t("-0.5j") == f(HyComplex(-0.5j))
assert t("1.e7j") == f(HyComplex(1e7j))
assert t("j") == f(HySymbol("j"))
assert isnan(t("NaNj")[0][1].imag)
assert t("nanj") == f(HySymbol("nanj"))
assert t("Inf+Infj") == f(HyComplex(complex(float("inf"), float("inf"))))
assert t("Inf-Infj") == f(HyComplex(complex(float("inf"), float("-inf"))))
assert t("Inf-INFj") == f(HySymbol("Inf_INFj"))
def test_lex_digit_separators():