From bb91b57dca88c035fcf27a1669b38597dad4de7f Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Sun, 14 May 2017 11:12:28 -0400 Subject: [PATCH] Require capitalizing NaN and Inf like so --- NEWS | 1 + docs/language/api.rst | 3 +++ hy/models.py | 31 +++++++++++++++++++++++++------ tests/test_lex.py | 22 ++++++++++++++++++++++ 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index a21c514..4c963f4 100644 --- a/NEWS +++ b/NEWS @@ -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 diff --git a/docs/language/api.rst b/docs/language/api.rst index 5ed646d..5241b3a 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -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 --------------- diff --git a/hy/models.py b/hy/models.py index 93cce18..77c580f 100644 --- a/hy/models.py +++ b/hy/models.py @@ -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 diff --git a/tests/test_lex.py b/tests/test_lex.py index 121eb9b..dfac923 100644 --- a/tests/test_lex.py +++ b/tests/test_lex.py @@ -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():