Extending Lua's string.gsub function with LPeg patterns
Lua's string.gsub
function is very useful, but it only works with Lua patterns, which are restricted in what you can express. For pattern matching, my usual approach when Lua patterns are not expressive enough is to use LPeg. LPeg doesn't come with a drop-in replacement for string.gsub
, but it is possible to construct an extended string.gsub
which accepts LPeg patterns, as shown below:
local lpeg = require "lpeg"
local original_gsub = string.gsub
function string.gsub(s, patt, repl)
if lpeg.type(patt) ~= "pattern" then
-- If patt isn't an LPEG pattern, revert to the normal gsub
return original_gsub(s, patt, repl)
else
-- Standardise repl to a function which takes the whole match
-- as the first argument, and then subsequent matches (if
-- there were any).
local typ = type(repl)
if typ == "table" then
local t = repl
repl = function(all, ...)
if select('#', ...) == 0 then
return t[all]
else
return t[...]
end
end
elseif typ == "string" then
local s = repl
repl = function(...)
local matches = {...}
return s:gsub("%%([0-9%%])", function(c)
if c == "%" then
return "%"
else
return matches[tonumber(c) + 1]
end
end)
end
elseif typ == "function" then
local f = repl
repl = function(all, ...)
if select('#', ...) == 0 then
return f(all)
else
return f(...)
end
end
else
error("Expected table / string / function")
end
return lpeg.Cs{
(lpeg.C(patt) / repl + 1) * lpeg.V(1) + true
}:match(s)
end
end