Repository: fredrikekre/Runic.jl (julia)

Issue:
TypeError when formatting floating point literal
Formatting https://github.com/simeonschaub/FredholmDeterminants.jl/blob/0bf41942e3206252fa6a95fdc4f7207b38e601b9/test/runtests.jl fails with the following error:

```
ERROR: failed to format input from test/runtests.jl: TypeError: in typeassert, expected RegexMatch, got a value of type Nothing
Stacktrace:
 [1] format_float_literals(ctx::Runic.Context, node::Runic.Node)
   @ Runic ~/.julia/packages/Runic/uu8ya/src/runestone.jl:101
 [2] format_node!(ctx::Runic.Context, node::Runic.Node)
   @ Runic ~/.julia/packages/Runic/uu8ya/src/Runic.jl:543
 [3] format_node_with_kids!(ctx::Runic.Context, node::Runic.Node)
   @ Runic ~/.julia/packages/Runic/uu8ya/src/Runic.jl:458
 [4] format_node!(ctx::Runic.Context, node::Runic.Node)
   @ Runic ~/.julia/packages/Runic/uu8ya/src/Runic.jl:570
 [5] format_node_with_kids!(ctx::Runic.Context, node::Runic.Node)
   @ Runic ~/.julia/packages/Runic/uu8ya/src/Runic.jl:458
```

Pull request:
Fix formatting of floating point literals that use `−` (Unicode U+2212)
instead of the typically used `-` (ASCII/Unicode U+002D). The parser
treats them as synonyms but the former triggered an assertion error in
Runic while formatting. After this change Runic will normalize to `-`
when formatting. Fixes #137.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bfe91e4..113048d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## Unreleased
+### Fixed
+ - Fix formatting of floating point literals that use `−` (Unicode U+2212) instead of the
+   typically used `-` (ASCII/Unicode U+002D). The parser treats them as synonyms but the
+   former triggered an assertion error in Runic while formatting. After this change Runic
+   will normalize to `-` when formatting ([#137], [#138]).
+
 ## [v1.4.2] - 2025-02-18
 ### Fixed
  - Add missing documentation of `--lines` to the `--help` output ([#136]).
@@ -122,6 +129,7 @@ First stable release of Runic.jl. See [README.md](README.md) for details and doc
 [v1.3.0]: https://github.com/fredrikekre/Runic.jl/releases/tag/v1.3.0
 [v1.4.0]: https://github.com/fredrikekre/Runic.jl/releases/tag/v1.4.0
 [v1.4.1]: https://github.com/fredrikekre/Runic.jl/releases/tag/v1.4.1
+[v1.4.2]: https://github.com/fredrikekre/Runic.jl/releases/tag/v1.4.2
 [#97]: https://github.com/fredrikekre/Runic.jl/issues/97
 [#108]: https://github.com/fredrikekre/Runic.jl/issues/108
 [#109]: https://github.com/fredrikekre/Runic.jl/issues/109
@@ -142,3 +150,5 @@ First stable release of Runic.jl. See [README.md](README.md) for details and doc
 [#132]: https://github.com/fredrikekre/Runic.jl/issues/132
 [#133]: https://github.com/fredrikekre/Runic.jl/issues/133
 [#136]: https://github.com/fredrikekre/Runic.jl/issues/136
+[#137]: https://github.com/fredrikekre/Runic.jl/issues/137
+[#138]: https://github.com/fredrikekre/Runic.jl/issues/138
diff --git a/src/runestone.jl b/src/runestone.jl
index 01564f2..5c84dd2 100644
--- a/src/runestone.jl
+++ b/src/runestone.jl
@@ -97,12 +97,15 @@ function format_float_literals(ctx::Context, node::Node)
         return nothing
     end
     # Split up the pieces
-    r = r"^(?<sgn>[+-])?(?<int>\d*)(?:\.?(?<frac>\d*))?(?:(?<epm>[eEf][+-]?)(?<exp>\d+))?$"
+    # Note that Julia considers '−' (Unicode U+2212) to be a synonym to the normally used
+    # '-' (ASCII/Unicode U+002D) so we need to check for the former in this regex, and
+    # normalize it to '-' when writing it out.
+    r = r"^(?<sgn>[+\-−])?(?<int>\d*)(?:\.?(?<frac>\d*))?(?:(?<epm>[eEf][+\-−]?)(?<exp>\d+))?$"
     m = match(r, str)::RegexMatch
     io = IOBuffer() # TODO: Could be reused?
     # Write the sign part
     if (sgn = m[:sgn]; sgn !== nothing)
-        write(io, sgn)
+        write(io, replace(sgn, "−" => "-")) # \u2212 => \u002D
     end
     # Strip leading zeros from integral part
     int_part = isempty(m[:int]) ? "0" : m[:int]
@@ -116,7 +119,7 @@ function format_float_literals(ctx::Context, node::Node)
     write(io, frac_part)
     # Write the exponent part
     if m[:epm] !== nothing
-        write(io, replace(m[:epm], "E" => "e"))
+        write(io, replace(m[:epm], "E" => "e", "−" => "-")) # \u2212 => \u002D
         @assert m[:exp] !== nothing
         # Strip leading zeros from integral part
         exp_part = isempty(m[:exp]) ? "0" : m[:exp]


Test patch:
diff --git a/test/runtests.jl b/test/runtests.jl
index 2607571..3b4ccd7 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -194,6 +194,11 @@ end
             end
         end
     end
+    # Issue #137: '−' (Unicode U+2212) is a synonym in the parser to the normally used
+    # '-' (ASCII/Unicode U+002D)
+    @test format_string("\u22121.0") == "-1.0"
+    @test format_string("1.0e\u22121") == "1.0e-1"
+    @test format_string("\u22121.0e\u22121") == "-1.0e-1"
 end
 
 @testset "whitespace between operators" begin