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

View File

@ -3,6 +3,7 @@
# license. See the LICENSE. # license. See the LICENSE.
from __future__ import unicode_literals from __future__ import unicode_literals
from math import isnan, isinf
from hy._compat import PY3, str_type, bytes_type, long_type, string_types from hy._compat import PY3, str_type, bytes_type, long_type, string_types
from fractions import Fraction from fractions import Fraction
@ -142,15 +143,24 @@ if not PY3: # do not add long on python3
_wrappers[long_type] = HyInteger _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): class HyFloat(HyObject, float):
""" """
Internal representation of a Hy Float. May raise a ValueError as if Internal representation of a Hy Float. May raise a ValueError as if
float(foo) was called, given HyFloat(foo). float(foo) was called, given HyFloat(foo).
""" """
def __new__(cls, number, *args, **kwargs): def __new__(cls, num, *args, **kwargs):
number = float(strip_digit_separators(number)) value = super(HyFloat, cls).__new__(cls, strip_digit_separators(num))
return super(HyFloat, cls).__new__(cls, number) check_inf_nan_cap(num, value)
return value
_wrappers[float] = HyFloat _wrappers[float] = HyFloat
@ -161,9 +171,18 @@ class HyComplex(HyObject, complex):
complex(foo) was called, given HyComplex(foo). complex(foo) was called, given HyComplex(foo).
""" """
def __new__(cls, number, *args, **kwargs): def __new__(cls, num, *args, **kwargs):
number = complex(strip_digit_separators(number)) value = super(HyComplex, cls).__new__(cls, strip_digit_separators(num))
return super(HyComplex, cls).__new__(cls, number) 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 _wrappers[complex] = HyComplex

View File

@ -2,6 +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.
from math import isnan
from hy.models import (HyExpression, HyInteger, HyFloat, HyComplex, HySymbol, from hy.models import (HyExpression, HyInteger, HyFloat, HyComplex, HySymbol,
HyString, HyDict, HyList, HySet, HyCons) HyString, HyDict, HyList, HySet, HyCons)
from hy.lex import LexException, PrematureEndOfInput, tokenize from hy.lex import LexException, PrematureEndOfInput, tokenize
@ -91,6 +92,22 @@ def test_lex_expression_float():
assert objs == [HyExpression([HySymbol("foo"), HyFloat(1.e7)])] 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(): def test_lex_expression_complex():
""" Make sure expressions can produce 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("-0.5j") == f(HyComplex(-0.5j))
assert t("1.e7j") == f(HyComplex(1e7j)) assert t("1.e7j") == f(HyComplex(1e7j))
assert t("j") == f(HySymbol("j")) 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(): def test_lex_digit_separators():