andreyorst@gmail.com writes:
> &inf and -&inf were added to represent positive and negative infinity.
> &nan and -&nan were added to represent positive and negative NaN (not
> a number) value. For some reason, in PUC Lua 0/0 gives -nan so in
> order to generate positive NaN portably across most Lua
> implementations, math.acos(2) is used. If there's a way to do it
> without depending on math table, it should be used instead, as math,
> theoretically, may be absent in some implementations. Theoretically,
> n%0 is always NaN, but it's an error in PUC Lua specifically.
This looks good! It's very weird that 0/0 is considered negative in Lua,
and boy there are a lot of quirks to work around from one supported
version to another, but I think this does a good job.
I had one thought; we can't have a dependency on fennel.compiler from
fennel.lua, but going the other way is actually OK because
fennel.compiler already loads fennel.utils which already loads fennel.view.
So it's OK to use fennel.view from the compiler; we can deal with the
discrepancies using the options table.
I've also factored out the exponential-notation part for readability.
What do you think of this on top of your changes?
-Phil
---
src/fennel/compiler.fnl | 40 +++++++++---------------------------src/fennel/view.fnl | 45 +++++++++++++++++++++++------------------
2 files changed, 35 insertions(+), 50 deletions(-)
diff --git a/src/fennel/compiler.fnl b/src/fennel/compiler.fnl
index 1e8eaf4..67f3ad3 100644
--- a/src/fennel/compiler.fnl+++ b/src/fennel/compiler.fnl
@@ -5,6 +5,7 @@
(local utils (require :fennel.utils))
(local parser (require :fennel.parser))
(local friend (require :fennel.friend))
+(local view (require :fennel.view))(local unpack (or table.unpack _G.unpack))
@@ -531,38 +532,17 @@ if opts contains the nval option."
(symbol-to-expression ast scope true))]
(handle-compile-opts [e] parent opts ast))))
-;; 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]- (let [val (if (not= n n)- (if (: (tostring n) :match "^%-") "(0/0)" "(-(0/0))")- (= (math.floor n) n)- (let [s1 (string.format "%.f" n)]- (if (= s1 (tostring (/ 1 0))) "(1/0)" ; portable inf- (= s1 (tostring (/ -1 0))) "(-1/0)"- (= s1 (tostring n)) s1 ; no precision loss- (or (faccumulate [s nil- i 0 308 ; beyond 308 every number turns to inf- :until s]- (let [s (string.format (.. "%." i "e") n)]- (when (= n (tonumber s))- (let [exp (s:match "e%+?(%d+)$")]- ;; Lua keeps numbers in standard notation up to e+14- (if (and exp (> (tonumber exp) 14))- s- s1)))))- s1)))- (tostring n))]- (pick-values 1 (string.gsub val "," "."))))+(local view-opts {:infinity "(1/0)" :negative-infinity "(-1/0)"+ ;; looks wrong but it's right; 0/0 -> -nan somehow?!+ :nan "(-(0/0))" :negative-nan "(0/0)"})(fn compile-scalar [ast _scope parent opts]
- (let [serialize (match (type ast)- :nil tostring- :boolean tostring- :string serialize-string- :number serialize-number)]- (handle-compile-opts [(utils.expr (serialize ast) :literal)] parent opts)))+ (let [compiled (case (type ast)+ :nil :nil+ :boolean (tostring ast)+ :string (serialize-string ast)+ :number (view ast view-opts))]+ (handle-compile-opts [(utils.expr compiled :literal)] parent opts)))(fn compile-table [ast scope parent opts compile1]
(fn escape-key [k]
diff --git a/src/fennel/view.fnl b/src/fennel/view.fnl
index 747223f..23e7511 100644
--- a/src/fennel/view.fnl+++ b/src/fennel/view.fnl
@@ -282,30 +282,35 @@
(set options.level (- options.level 1))
x))
-;; A modified copy of compiler.serialize-number that doesn't handle-;; the infinity cases-(fn number->string [n]- ;; Transform number to a string without depending on correct `os.locale`- ;; Makes best effort to keep the original notation of the number.+;; sadly luajit tostring is imprecise https://todo.sr.ht/~technomancy/fennel/231+(fn exponential-notation [n fallback]+ (faccumulate [s nil+ i 0 308 ; beyond 308 every number turns to inf+ :until s]+ (let [s (string.format (.. "%." i "e") n)]+ (when (= n (tonumber s))+ (let [exp (s:match "e%+?(%d+)$")]+ ;; Lua keeps numbers in standard notation up to e+14+ (if (and exp (< 14 (tonumber exp)))+ s+ fallback))))))++(local inf-str (tostring (/ 1 0)))+(local neg-inf-str (tostring (/ -1 0)))++(fn number->string [n options] (let [val (if (not= n n)
- (if (: (tostring n) :match "^%-") "-&nan" "&nan")+ (if (= 45 (string.byte (tostring n))) ; -+ (or options.negative-nan "-&nan")+ (or options.nan "&nan")) (= (math.floor n) n)
(let [s1 (string.format "%.f" n)]
- (if (= s1 (tostring (/ 1 0))) "&inf"- (= s1 (tostring (/ -1 0))) "-&inf"+ (if (= s1 inf-str) (or options.infinity "&inf")+ (= s1 neg-inf-str) (or options.negative-infinity "-&inf") (= s1 (tostring n)) s1 ; no precision loss
- (or (faccumulate [s nil- i 0 308 ; beyond 308 every number turns to inf- :until s]- (let [s (string.format (.. "%." i "e") n)]- (when (= n (tonumber s))- (let [exp (s:match "e%+?(%d+)$")]- ;; Lua keeps numbers in standard notation up to e+14- (if (and exp (> (tonumber exp) 14))- s- s1)))))- s1)))+ (or (exponential-notation n s1) s1))) (tostring n))]
+ ;; Transform number to a string without depending on correct `os.locale` (pick-values 1 (string.gsub val "," "."))))
(fn colon-string? [s]
@@ -418,7 +423,7 @@ as numeric escapes rather than letter-based escapes, which is ugly."
(case (getmetatable x) {: __fennelview} __fennelview)))
(pp-table x options indent)
(= tv :number)
- (number->string x)+ (number->string x options) (and (= tv :string) (colon-string? x)
(if (not= colon? nil) colon?
(= :function (type options.prefer-colon?)) (options.prefer-colon? x)
--
2.39.2
Aug 16, 2024 23:47:47 Phil Hagelberg <phil@hagelb.org>:
> andreyorst@gmail.com writes:>>> &inf and -&inf were added to represent positive and negative infinity.>> &nan and -&nan were added to represent positive and negative NaN (not>> a number) value. For some reason, in PUC Lua 0/0 gives -nan so in>> order to generate positive NaN portably across most Lua>> implementations, math.acos(2) is used. If there's a way to do it>> without depending on math table, it should be used instead, as math,>> theoretically, may be absent in some implementations. Theoretically,>> n%0 is always NaN, but it's an error in PUC Lua specifically.>> This looks good! It's very weird that 0/0 is considered negative in > Lua,> and boy there are a lot of quirks to work around from one supported> version to another, but I think this does a good job.
I have an idea on more portable solution without the math table.
> I had one thought; we can't have a dependency on fennel.compiler from> fennel.lua, but going the other way is actually OK because> fennel.compiler already loads fennel.utils which already loads > fennel.view.> So it's OK to use fennel.view from the compiler; we can deal with the> discrepancies using the options table.
Good to know!
> I've also factored out the exponential-notation part for readability.>> What do you think of this on top of your changes?
Thanks! I'll get vack to it in a few days, and look closer.
We had an interesting discussion today on the Fennel meetup call. Some
people were surprised that we could have `&inf` as the notation for
infinity because the current precedent for ampersand is to use it for
directives like `&as`, `&into`, or `&until`, and this uses a similar
notation but a very different meaning.
The original intent of marking `&` as reserved was just to have a
character that's guaranteed not to ever be a valid identifier in scope,
but the perception or common understanding of it is that it's for
directives.
So we started talking about alternatives which might be clearer. One
that was suggested was `.inf` because the dot suggests a numeric
meaning. Another suggestion was `&:inf` which looks different enough
from `&into`; the colon makes it look more "value-like", similarly to
colon strings.
Would like to hear what you think about that.
-Phil
Aug 17, 2024 23:23:32 Phil Hagelberg <phil@hagelb.org>:
> We had an interesting discussion today on the Fennel meetup call. Some> people were surprised that we could have `&inf` as the notation for> infinity because the current precedent for ampersand is to use it for> directives like `&as`, `&into`, or `&until`, and this uses a similar> notation but a very different meaning
I mentioned that in the ticket too.
> The original intent of marking `&` as reserved was just to have a> character that's guaranteed not to ever be a valid identifier in scope,> but the perception or common understanding of it is that it's for> directives.>> So we started talking about alternatives which might be clearer. One> that was suggested was `.inf` because the dot suggests a numeric> meaning. Another suggestion was `&:inf` which looks different enough> from `&into`; the colon makes it look more "value-like", similarly to> colon strings.>> Would like to hear what you think about that.
Huh, I didn't know that &into is a thing, always used :into. &:inf is a
mix of two notations, even more confusing. W also have @ for reader
macros, maybe it's ok to use it here? @inf -@inf.
dot notation seems ok as well.