Merge pull request #1294 from Kodiologist/nan-and-inf
Require capitalizing NaN and Inf like so
This commit is contained in:
commit
5610d7dedf
1
NEWS
1
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
|
||||
|
@ -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
|
||||
---------------
|
||||
|
||||
|
@ -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
|
||||
|
31
hy/models.py
31
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
|
||||
|
||||
|
@ -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]"
|
||||
|
@ -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():
|
||||
|
Loading…
x
Reference in New Issue
Block a user