Repository: hylang/hy

Issue:
Any capitalization of `NaN` and `Inf` are parsed as imaginary numbers with uppercase suffix `J`
This issue seems related to #1288 and #1294. Floating-point special values such as `NaN` and `Inf` are required to be capitalized exactly as such, but when used in imaginary number literals with _uppercase_ `J` suffix, any capitalization is allowed. In the same spirit as #1288, I believe this is unintended/undesired behavior.

```hy
NaNj ; valid (good)
NANj ; invalid (good)
Infj ; valid (good)
iNfj ; invalid (good)

NaNJ ; valid (good)
NANJ ; valid (should not be!)
InfJ ; valid (good)
iNfJ ; valid (should not be!)
```

Interestingly enough, this behavior only occurs in pure-imaginary literals; it doesn't surface in "compound" complex literals, which seem to have the correct behavior:

```hy
1+NaNj ; valid (good)
1+NANJ ; invalid (good)
```

Discussion:

Patch:
diff --git a/NEWS.rst b/NEWS.rst
index 61d9a3fb..d167fe40 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -50,6 +50,8 @@ Bug Fixes
 * Fixed incomplete recognition of macro calls with a unary dotted
   head like `((. defn) f [])`.
 * `~@ #*` now produces a syntax error instead of a nonsensical result.
+* Fixed parsing of infinite and NaN imaginary literals with an
+  uppercase "J".
 * Fixed `hy.eval` failing on `defreader` or `require` forms that
   install a new reader.
 * `require` now warns when you shadow a core macro, like `defmacro`
diff --git a/docs/syntax.rst b/docs/syntax.rst
index 1a55eb7a..54188fda 100644
--- a/docs/syntax.rst
+++ b/docs/syntax.rst
@@ -152,7 +152,8 @@ few extensions:
   interpreted in octal like they do in C. For octal, use the prefix ``0o``, as
   in Python.
 - ``NaN``, ``Inf``, and ``-Inf`` are understood as literals. Each produces a
-  :class:`Float <hy.models.Float>`.
+  :class:`Float <hy.models.Float>`. These are case-sensitive, unlike other uses
+  of letters in numeric literals (``1E2``, ``0XFF``, ``5J``, etc.).
 - Hy allows complex literals as understood by the constructor for
   :class:`complex`, such as ``5+4j``. (This is also legal Python, but Hy reads
   it as a single :class:`Complex <hy.models.Complex>`, and doesn't otherwise
diff --git a/hy/models.py b/hy/models.py
index 3ce3a74e..23ccb6c5 100644
--- a/hy/models.py
+++ b/hy/models.py
@@ -361,7 +361,7 @@ class Complex(Object, complex):
         if isinstance(real, str):
             value = super().__new__(cls, strip_digit_separators(real))
             p1, _, p2 = real.lstrip("+-").replace("-", "+").partition("+")
-            check_inf_nan_cap(p1, value.imag if "j" in p1 else value.real)
+            check_inf_nan_cap(p1, value.imag if "j" in p1.lower() else value.real)
             if p2:
                 check_inf_nan_cap(p2, value.imag)
             return value


Test patch:
diff --git a/tests/test_reader.py b/tests/test_reader.py
index 6127a11c..3ae60daf 100644
--- a/tests/test_reader.py
+++ b/tests/test_reader.py
@@ -225,9 +225,13 @@ def test_lex_expression_complex():
     def f(x):
         return [Expression([Symbol("foo"), x])]
 
+    assert t("2j") == f(Complex(2.0j))
+    assert t("2J") == f(Complex(2.0j))
     assert t("2.j") == f(Complex(2.0j))
+    assert t("2.J") == f(Complex(2.0j))
     assert t("-0.5j") == f(Complex(-0.5j))
     assert t("1.e7j") == f(Complex(1e7j))
+    assert t("1.e7J") == f(Complex(1e7j))
     assert t("j") == f(Symbol("j"))
     assert t("J") == f(Symbol("J"))
     assert isnan(t("NaNj")[0][1].imag)
@@ -236,6 +240,13 @@ def test_lex_expression_complex():
     assert t("Inf-Infj") == f(Complex(complex(float("inf"), float("-inf"))))
     assert t("Inf-INFj") == f(Symbol("Inf-INFj"))
 
+    # https://github.com/hylang/hy/issues/2521
+    assert isnan(t("NaNJ")[0][1].imag)
+    assert t("nanJ") == f(Symbol("nanJ"))
+    assert t("InfJ") == f(Complex(complex(0, float("inf"))))
+    assert t("iNfJ") == f(Symbol("iNfJ"))
+    assert t("Inf-INFJ") == f(Symbol("Inf-INFJ"))
+
 
 def test_lex_digit_separators():