Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
manual fix for Ryan's stringx.quote_string
  • Loading branch information
Steve Donovan committed Mar 3, 2015
2 parents 1967c52 + 2945226 commit 4a0a7e9
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 9 deletions.
45 changes: 36 additions & 9 deletions lua/pl/pretty.lua
Expand Up @@ -9,8 +9,25 @@ local append = table.insert
local concat = table.concat
local utils = require 'pl.utils'
local lexer = require 'pl.lexer'
local quote_string = require'pl.stringx'.quote_string
local assert_arg = utils.assert_arg

--AAS
--Perhaps this could be evolved into part of a "Compat5.3" library some day.
--I didn't think that it was time for that, however.
local tostring = tostring
if _VERSION == "Lua 5.3" then
local _tostring = tostring
tostring = function(s)
if type(s) == "number" then
return ("%.f"):format(s)
else
return _tostring(s)
end
end

end

local pretty = {}

local function save_string_index ()
Expand Down Expand Up @@ -93,7 +110,8 @@ end
local function quote_if_necessary (v)
if not v then return ''
else
if v:find ' ' then v = '"'..v..'"' end
--AAS
if v:find ' ' then v = quote_string(v) end
end
return v
end
Expand All @@ -108,12 +126,17 @@ local function quote (s)
if type(s) == 'table' then
return pretty.write(s,'')
else
return ('%q'):format(tostring(s))
--AAS
return quote_string(s)-- ('%q'):format(tostring(s))
end
end

local function index (numkey,key)
if not numkey then key = quote(key) end
--AAS
if not numkey then
key = quote(key)
key = key:find("^%[") and (" " .. key .. " ") or key
end
return '['..key..']'
end

Expand Down Expand Up @@ -178,11 +201,13 @@ function pretty.write (tbl,space,not_clever)
if tp ~= 'string' and tp ~= 'table' then
putln(quote_if_necessary(tostring(t))..',')
elseif tp == 'string' then
if t:find('\n') then
putln('[[\n'..t..']],')
else
putln(quote(t)..',')
end
-- if t:find('\n') then
-- putln('[[\n'..t..']],')
-- else
-- putln(quote(t)..',')
-- end
--AAS
putln(quote_string(t) ..",")
elseif tp == 'table' then
if tables[t] then
putln('<cycle>,')
Expand Down Expand Up @@ -245,7 +270,9 @@ local memp,nump = {'B','KiB','MiB','GiB'},{'','K','M','B'}
local comma
function comma (val)
local thou = math.floor(val/1000)
if thou > 0 then return comma(thou)..','..(val % 1000)
--AAS
if thou > 0 then return comma(tostring(thou))..','.. tostring(val % 1000)
-- if thou > 0 then return comma(thou)..','..(val % 1000)
else return tostring(val) end
end

Expand Down
48 changes: 48 additions & 0 deletions lua/pl/stringx.lua
Expand Up @@ -454,6 +454,54 @@ function stringx.shorten(self,sz,tail)
return self
end

--- Utility function that finds any patterns that match a long string's an open or close.
-- Note that having this function use the least number of equal signs that is possible is a harder algorithm to come up with.
-- Right now, it simply returns the greatest number of them found.
-- @param s The string
-- @return 'nil' if not found. If found, the maximum number of equal signs found within all matches.
local function has_lquote(s)
local lstring_pat = '([%[%]])(=*)%1'
local start, finish, bracket, equals, next_equals = nil, 0, nil, nil, nil
-- print("checking lquote for", s)
repeat
start, finish, bracket, next_equals = s:find(lstring_pat, finish + 1)
if start then
-- print("found start", start, finish, bracket, next_equals)
--length of captured =. Ex: [==[ is 2, ]] is 0.
next_equals = #next_equals
equals = next_equals >= (equals or 0) and next_equals or equals
end
until not start
--next_equals will be nil if there was no match.
return equals
end

--- Quote the given string and preserve any control or escape characters, such that reloading the string in Lua returns the same result.
-- @param s The string to be quoted.
-- @return The quoted string.
function stringx.quote_string(s)
--find out if there are any embedded long-quote
--sequences that may cause issues.
--This is important when strings are embedded within strings, like when serializing.
local equal_signs = has_lquote(s)
if s:find("\n") or equal_signs then
-- print("going with long string:", s)
equal_signs = ("="):rep((equal_signs or -1) + 1)
--long strings strip out leading \n. We want to retain that, when quoting.
if s:find("^\n") then s = "\n" .. s end
--if there is an embedded sequence that matches a long quote, then
--find the one with the maximum number of = signs and add one to that number
local lbracket, rbracket =
"[" .. equal_signs .. "[",
"]" .. equal_signs .. "]"
s = lbracket .. s .. rbracket
else
--Escape funny stuff.
s = ("%q"):format(s)
end
return s
end

function stringx.import(dont_overload)
utils.import(stringx,string)
end
Expand Down
60 changes: 60 additions & 0 deletions tests/test-stringx.lua
Expand Up @@ -270,3 +270,63 @@ asserteq(stringx.strip(' hello '),'hello')
asserteq(stringx.strip('--[hello] -- - ','-[] '),'hello')
asserteq(stringx.rstrip('--[hello] -- - ','-[] '),'--[hello')

--


local assert_str_round_trip = function(s)

local qs = stringx.quote_string(s)
local compiled, err = load("return "..qs)

if not compiled then
print(
("stringx.quote_string assert failed: invalid string created: Received:\n%s\n\nCompiled to\n%s\n\nError:\t%s\n"):
format(s, qs, err)
)
error()
else
compiled = compiled()
end

if compiled ~= s then
print("strinx.quote_string assert Failed: String compiled but did not round trip.")
print("input string:\t\t",s, #s)
print("compiled string:\t", compiled, #compiled)
print("output string:\t\t",qs, #qs)
error()
else
-- print("input string:\t\t",s)
-- print("compiled string:\t", compiled)
-- print("output string:\t\t",qs)
end
end

assert_str_round_trip( "normal string with nothing weird.")
assert_str_round_trip( "Long string quoted with escaped quote \\\" and a long string pattern match [==[ found near the end.")

assert_str_round_trip( "Unescapped quote \" in the middle")
assert_str_round_trip( "[[Embedded long quotes \\\". Escaped must stay! ]]")
assert_str_round_trip( [[Long quoted string with a slash prior to quote \\\". ]])
assert_str_round_trip( "[[Completely normal\n long quote. ]]")
assert_str_round_trip( "\n[[Completely normal\n long quote. Except that we lead with a return! Tricky! ]]")
assert_str_round_trip( '"balance [======[ doesn\'t ]====] mater when searching for embedded long-string quotes.')
assert_str_round_trip( "Any\0 \t control character other than a return will be handled by the %q mechanism.")
assert_str_round_trip( "This\tincludes\ttabs.")
assert_str_round_trip( "But not returns.\n Returns are easier to see using long quotes.")
assert_str_round_trip( "The \z
escape does not trigger a control pattern, however.")

assert_str_round_trip( "[==[If a string is long-quoted, escaped \\\" quotes have to stay! ]==]")
assert_str_round_trip('"A quoted string looks like what?"')
assert_str_round_trip( "'I think that it should be quoted, anyway.'")
assert_str_round_trip( "[[Even if they're long quoted.]]")

assert_str_round_trip( "\"\\\"\\' pathalogical:starts with a quote ]\"\\']=]]==][[]]]=========]")
assert_str_round_trip( "\\\"\\\"\\' pathalogical: quote is after this text with a quote ]\"\\']=]]==][[]]]=========]")
assert_str_round_trip( "\\\"\\\"\\' pathalogical: quotes are all escaped. ]\\\"\\']=]]==][[]]]=========]")
assert_str_round_trip( "")
assert_str_round_trip( " ")
assert_str_round_trip( "\n") --tricky.
assert_str_round_trip( "[[")
assert_str_round_trip( "''")
assert_str_round_trip( '""')

0 comments on commit 4a0a7e9

Please sign in to comment.