~technomancy/fennel

fennel: Make best effort at keeping the number format when compiling ints v1 PROPOSED

: 1
 Make best effort at keeping the number format when compiling ints

 5 files changed, 52 insertions(+), 8 deletions(-)
#1301751 .build.yml success
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~technomancy/fennel/patches/54442/mbox | git am -3
Learn more about email & git

[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