Merge pull request #1294 from Kodiologist/nan-and-inf

Require capitalizing NaN and Inf like so
This commit is contained in:
Kodi Arfer 2017-07-10 08:54:38 -07:00 committed by GitHub
commit 5610d7dedf
6 changed files with 83 additions and 21 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

@ -2,8 +2,10 @@
;; This file is part of Hy, which is free software licensed under the Expat
;; license. See the LICENSE.
(import [hy._compat [PY3 str-type bytes-type long-type]])
(import [hy.models [HyObject HyExpression HySymbol HyKeyword HyInteger HyList HyDict HySet HyString HyBytes]])
(import
[math [isnan]]
[hy._compat [PY3 str-type bytes-type long-type]]
[hy.models [HyObject HyExpression HySymbol HyKeyword HyInteger HyFloat HyComplex HyList HyDict HySet HyString HyBytes]])
(defn hy-repr [obj]
(setv seen (set))
@ -72,8 +74,14 @@
(.format "(int {})" (repr x))
(and (not PY3) (in t [long_type HyInteger]))
(.rstrip (repr x) "L")
(is t complex)
(.strip (repr x) "()")
(and (in t [float HyFloat]) (isnan x))
"NaN"
(= x Inf)
"Inf"
(= x -Inf)
"-Inf"
(in t [complex HyComplex])
(.replace (.replace (.strip (repr x) "()") "inf" "Inf") "nan" "NaN")
(is t fraction)
(.format "{}/{}" (f x.numerator q) (f x.denominator q))
; else

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

@ -3,16 +3,17 @@
;; license. See the LICENSE.
(import
[math [isnan]]
[hy.contrib.hy-repr [hy-repr]])
(defn test-hy-repr-roundtrip-from-value []
; Test that a variety of values round-trip properly.
(setv values [
None False True
5 5.1 '5 '5.1
5 5.1 '5 '5.1 Inf -Inf
(int 5)
1/2
5j 5.1j 2+1j 1.2+3.4j
5j 5.1j 2+1j 1.2+3.4j Inf-Infj
"" b""
'"" 'b""
"apple bloom" b"apple bloom" "⚘"
@ -32,10 +33,17 @@
(for [original-val values]
(setv evaled (eval (read-str (hy-repr original-val))))
(assert (= evaled original-val))
(assert (is (type evaled) (type original-val)))))
(assert (is (type evaled) (type original-val))))
(assert (isnan (eval (read-str (hy-repr NaN))))))
(defn test-hy-repr-roundtrip-from-str []
(setv strs [
"'Inf"
"'-Inf"
"'NaN"
"1+2j"
"NaN+NaNj"
"'NaN+NaNj"
"[1 2 3]"
"'[1 2 3]"
"[1 'a 3]"

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,16 +92,38 @@ 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 """
objs = tokenize("(foo 2.j)")
assert objs == [HyExpression([HySymbol("foo"), HyComplex(2.j)])]
objs = tokenize("(foo -0.5j)")
assert objs == [HyExpression([HySymbol("foo"), HyComplex(-0.5j)])]
objs = tokenize("(foo 1.e7j)")
assert objs == [HyExpression([HySymbol("foo"), HyComplex(1.e7j)])]
objs = tokenize("(foo j)")
assert objs == [HyExpression([HySymbol("foo"), HySymbol("j")])]
def t(x): return tokenize("(foo {})".format(x))
def f(x): return [HyExpression([HySymbol("foo"), x])]
assert t("2.j") == f(HyComplex(2.j))
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():