From 6e41f2ca07ba7f7a2ca97569b1917f6bcd116e39 Mon Sep 17 00:00:00 2001 From: Paul Kulchenko Date: Thu, 5 Nov 2020 21:04:41 -0800 Subject: [PATCH] Added option to change radix for floating numbers (closes #36, closes #37). This is needed to produce a valid Lua code when a local locale setting makes `format` to produce floating point numbers with a comma. Limitation: only commas are being fixed in floating point numbers. --- README.md | 1 + src/serpent.lua | 11 +++++++++-- t/test.lua | 18 ++++++++++++++---- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ca49ea5..007b0cc 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ Similar to `pcall` and `loadstring` calls, `load` returns status as the first va * sparse (true/false) -- force sparse encoding (no nil filling based on `#t`). * compact (true/false) -- remove spaces. * fatal (true/False) -- raise fatal error on non-serilizable values. +* fixradix (true/False) -- change radix character set depending on locale to decimal dot. * nocode (true/False) -- disable bytecode serialization for easy comparison. * nohuge (true/False) -- disable checking numbers against undefined and huge values. * maxlevel (number) -- specify max level up to which to expand nested tables. diff --git a/src/serpent.lua b/src/serpent.lua index a043713..942b814 100644 --- a/src/serpent.lua +++ b/src/serpent.lua @@ -1,4 +1,4 @@ -local n, v = "serpent", "0.302" -- (C) 2012-18 Paul Kulchenko; MIT License +local n, v = "serpent", "0.303" -- (C) 2012-18 Paul Kulchenko; MIT License local c, d = "Paul Kulchenko", "Lua serializer and pretty printer" local snum = {[tostring(1/0)]='1/0 --[[math.huge]]',[tostring(-1/0)]='-1/0 --[[-math.huge]]',[tostring(0/0)]='0/0'} local badtype = {thread = true, userdata = true, cdata = true} @@ -23,9 +23,16 @@ local function s(t, opts) local function gensym(val) return '_'..(tostring(tostring(val)):gsub("[^%w]",""):gsub("(%d%w+)", -- tostring(val) is needed because __tostring may return a non-string value function(s) if not syms[s] then symn = symn+1; syms[s] = symn end return tostring(syms[s]) end)) end - local function safestr(s) return type(s) == "number" and tostring(huge and snum[tostring(s)] or numformat:format(s)) + local function safestr(s) return type(s) == "number" and (huge and snum[tostring(s)] or numformat:format(s)) or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026 or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end + -- handle radix changes in some locales + if opts.fixradix and (".1f"):format(1.2) ~= "1.2" then + local origsafestr = safestr + safestr = function(s) return type(s) == "number" + and (nohuge and snum[tostring(s)] or numformat:format(s):gsub(",",".")) or origsafestr(s) + end + end local function comment(s,l) return comm and (l or 0) < comm and ' --[['..select(2, pcall(tostring, s))..']]' or '' end local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal and safestr(select(2, pcall(tostring, s))) or error("Can't serialize "..tostring(s)) end diff --git a/t/test.lua b/t/test.lua index 995aa1b..eecf39d 100644 --- a/t/test.lua +++ b/t/test.lua @@ -328,13 +328,23 @@ end do local a = serpent.line({ pi = math.pi }) local ok, t = serpent.load(a) - assert(ok, "pi is serialized and deserialized (1/2): failed") - assert(math.pi == t.pi, "pi is deserialized with the same value as the original (1/2): failed") + assert(ok, "pi is serialized and deserialized (1/3): failed") + assert(math.pi == t.pi, "pi is deserialized with the same value as the original (1/3): failed") a = serpent.line({pi = math.pi}, {numformat = "%1.16e"}) ok, t = serpent.load(a) - assert(ok, "pi is serialized and deserialized (2/2): failed") - assert(math.pi == t.pi, "pi is deserialized with the same value as the original with '%1.16e' format (2/2): failed") + assert(ok, "pi is serialized and deserialized (2/3): failed") + assert(math.pi == t.pi, "pi is deserialized with the same value as the original with '%1.16e' format (2/3): failed") + + local locale = os.setlocale() + os.setlocale("German_Germany") + + a = serpent.line({pi = math.pi}, {fixradix = true}) + ok, t = serpent.load(a) + assert(ok, "pi is serialized and deserialized with fr_FR locale (3/3): failed") + assert(math.pi == t.pi, "pi is deserialized with the same value as the original with fr_FR locale (3/3): failed") + + os.setlocale(locale) end -- based on https://gist.github.com/mpeterv/8360307