[PATCH fennel] Make best effort at keeping the number format when compiling ints
Export this patch
From: Andrey Listopadov <andreyorst@gmail.com>
---
changelog.md | 1 +
src/fennel/compiler.fnl | 19 ++++++++++++++++ ---
src/fennel/view.fnl | 21 ++++++++++++++++++ ---
test/core.fnl | 9 ++++++++ -
test/parser.fnl | 10 +++++++++ -
5 files changed, 52 insertions(+), 8 deletions(-)
diff --git a/changelog.md b/changelog.md
index 560fcf3..398af18 100644
--- a/changelog.md
+++ b/changelog.md
@@ -15,6 +15,7 @@ deprecated forms.
* Macro quote expansion no longer breaks when `sym`, `list` or `sequence` is shadowed
* Bring `fennel.traceback` behavior closer to Lua's `traceback` by
not modifying non-string and non-`nil` values.
+ * Avoid losing precision when compiling large numbers on LuaJIT.
## 1.5.0 / 2024-06-23
diff --git a/src/fennel/compiler.fnl b/src/fennel/compiler.fnl
index 0faf74b..e3e3774 100644
--- a/src/fennel/compiler.fnl
+++ b/src/fennel/compiler.fnl
@@ -533,10 +533,23 @@ (fn compile-sym
;; We do gsub transformation because some locales use , for
;; decimal separators, which will not be accepted by Lua.
+ ;; Makes best effort to keep the original notation of the number.
(fn serialize-number [n]
- (if (= (math.floor n) n)
- (string.format "%d" n)
- (string.gsub (tostring n) "," ".")))
+ (let [val (if (= (math.floor n) n)
+ (let [s1 (string.format "%.f" n)]
+ (if (= s1 (tostring n)) s1
+ (faccumulate [s nil i 0 99 :until s]
+ (let [s (string.format (.. "%." i "e") n)
+ n* (tonumber s)]
+ (when (= n n*)
+ (let [exp (s:match "e%+?(%d+)$")]
+ ;; Lua stops transforming numbers from
+ ;; e-notation to integers at e+14
+ (if (and exp (> (tonumber exp) 14))
+ s
+ s1)))))))
+ (tostring n))]
+ (pick-values 1 (string.gsub val "," "."))))
(fn compile-scalar [ast _scope parent opts]
(let [serialize (match (type ast)
diff --git a/src/fennel/view.fnl b/src/fennel/view.fnl
index 08ef7f8..781d827 100644
--- a/src/fennel/view.fnl
+++ b/src/fennel/view.fnl
@@ -282,11 +282,26 @@ (fn pp-table
(set options.level (- options.level 1))
x))
+ ;; A copy of compiler.serialize-number because fennel.view can't have
+ ;; dependencies
(fn number->string [n]
;; Transform number to a string without depending on correct `os.locale`
- (if (= (math.floor n) n)
- (string.format "%d" n)
- (pick-values 1 (string.gsub (tostring n) "," "."))))
+ ;; Makes best effort to keep the original notation of the number.
+ (let [val (if (= (math.floor n) n)
+ (let [s1 (string.format "%.f" n)]
+ (if (= s1 (tostring n)) s1
+ (faccumulate [s nil i 0 99 :until s]
+ (let [s (string.format (.. "%." i "e") n)
+ n* (tonumber s)]
+ (when (= n n*)
+ (let [exp (s:match "e%+?(%d+)$")]
+ ;; Lua stops transforming numbers from
+ ;; e-notation to integers at e+14
+ (if (and exp (> (tonumber exp) 14))
+ s
+ s1)))))))
+ (tostring n))]
+ (pick-values 1 (string.gsub val "," "."))))
(fn colon-string? [s]
;; Test if given string is valid colon string.
diff --git a/test/core.fnl b/test/core.fnl
index 4b138b5..4b9ed83 100644
--- a/test/core.fnl
+++ b/test/core.fnl
@@ -561,7 +561,14 @@ (fn test-with-open
["asdf" "closed file" "closed file"])
(== [(with-open [proc1 (io.popen "echo hi") proc2 (io.popen "echo bye")]
(values (proc1:read) (proc2:read)))]
- ["hi" "bye"]))
+ ["hi" "bye"])
+ (== (do
+ (var fh nil)
+ (local (ok msg) (pcall #(with-open [f (io.tmpfile)]
+ (set fh f)
+ (error {:bork! :bark!}))))
+ [(io.type fh) ok (case msg {:bork! :bark!} msg _ "didn't match")])
+ ["closed file" false {:bork! :bark!}]))
(fn test-comment []
(t.= "--[[ hello world ]]\nreturn nil"
diff --git a/test/parser.fnl b/test/parser.fnl
index 57df216..ac62cb6 100644
--- a/test/parser.fnl
+++ b/test/parser.fnl
@@ -20,8 +20,16 @@ (fn test-basics
[((fennel.parser (fennel.string-stream "&abc ")))])
(t.= "141791343654238"
(fennel.view (fennel.eval "141791343654238")))
+ (t.= "141791343654238"
+ (fennel.view (fennel.eval "1.41791343654238e+14")))
+ (t.= "1.41791343654238e+15"
+ (fennel.view (fennel.eval "1.41791343654238e+15")))
(t.= "14179134365.125"
- (fennel.view (fennel.eval "14179134365.125"))))
+ (fennel.view (fennel.eval "14179134365.125")))
+ (t.= "2.3456789012e+76"
+ (fennel.view (fennel.eval "2.3456789012e+76")))
+ (t.= "1.23456789e-13"
+ (fennel.view (fennel.eval "1.23456789e-13"))))
(fn test-comments []
(let [(ok? ast) ((fennel.parser (fennel.string-stream ";; abc")
--
2.45.0
fennel/patches/.build.yml: SUCCESS in 32s
[Make best effort at keeping the number format when compiling ints][0] from [][1]
[0]: https://lists.sr.ht/~technomancy/fennel/patches/54442
[1]: mailto:andreyorst@gmail.com
✓ #1301751 SUCCESS fennel/patches/.build.yml https://builds.sr.ht/~technomancy/job/1301751