Skip to content

Support EDN symbolic values (##Inf/##-Inf/##NaN) so non-finite floats round-trip#96

Merged
bfontaine merged 1 commit into
swaroopch:masterfrom
gaoflow:support-symbolic-values-inf-nan
Jun 17, 2026
Merged

Support EDN symbolic values (##Inf/##-Inf/##NaN) so non-finite floats round-trip#96
bfontaine merged 1 commit into
swaroopch:masterfrom
gaoflow:support-symbolic-values-inf-nan

Conversation

@gaoflow

@gaoflow gaoflow commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Problem

dumps() silently corrupts non-finite floats. inf, -inf, and nan are emitted as the bare tokens inf / -inf / nan, which the reader then reads back as Symbol objects rather than floats:

>>> import edn_format as e
>>> e.loads(e.dumps(float("inf")))
Symbol(inf)          # expected: inf
>>> e.loads("##Inf")
EDNDecodeError: Illegal character '#' ...

So loads(dumps(x)) does not round-trip for non-finite floats (the repo's own check_roundtrip invariant), and the canonical EDN representation produced by Clojure (##Inf / ##-Inf / ##NaN) cannot be read at all.

Fix

Support EDN's symbolic values, ##Inf / ##-Inf / ##NaN, on both paths:

  • edn_lex.py: a SYMBOLIC_VALUE token (\#\#(-Inf|Inf|NaN)), defined ahead of the other # token rules so it takes lexer priority. It maps to the matching float.
  • edn_parse.py: SYMBOLIC_VALUE added to the p_term_leaf rule.
  • edn_dump.py: non-finite floats now dump to ##Inf / ##-Inf / ##NaN instead of bare tokens.

Non-finite floats now round-trip, and ##Inf / ##-Inf / ##NaN from real EDN parse correctly. The non-finite check uses math.isnan / math.isinf (available on Python 2.6+) to stay within the package's stated version support.

Tests

Added test_symbolic_values to EdnTest, covering both parse and dump directions plus the existing check_roundtrip helper. It fails before the change (EDNDecodeError) and passes after. Full suite: 39 tests pass; flake8 --max-line-length=100 clean. Existing #-dispatch syntax (#{...}, #_, #inst, #uuid, #:ns{...}) is unaffected.

I used an AI assistant to help investigate this under my direction, and I have reviewed and verified the change myself.

dumps() emitted bare inf/-inf/nan for non-finite floats, which the reader
then read back as Symbol objects instead of floats: silent data corruption
with no error. ##Inf/##-Inf/##NaN coming from real EDN could not be read at
all (the lexer rejected the leading #).

Emit and read Clojure's symbolic values ##Inf/##-Inf/##NaN
(https://clojure.org/reference/reader#_symbolic_values) via a SYMBOLIC_VALUE
lexer token ordered ahead of the other # tokens, a grammar leaf, and a dump
branch, so loads(dumps(x)) round-trips inf/-inf/nan.
@bfontaine bfontaine merged commit fa3078f into swaroopch:master Jun 17, 2026
7 checks passed
@bfontaine

Copy link
Copy Markdown
Collaborator

Merged; thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants