Reputation: 69944
Given a LPeg grammar, I want to do a find/replace on some of the sub-matches.
For example, suppose that I want to replace all the occurrences of the letter "a" that are outside of double quotes. I came up with a grammar for that:
local re = require "re"
local pattern = re.compile([[
line <- (quoted / 'a' / .)*
quoted <- '"' rest
rest <- '"' / . rest
]])
It seems that re.gsub
is not useful here, because it would mess with the parts inside quotes. The best I could come up with was to use a table capture, where we capture everything besides the "a". But it was unwieldy because the pattern returns a table instead of a string.
line <- {| ({quoted} / 'a' / {.})* |}
I also looked into substitution captures, but got stuck because I needed to add ->'%1'
around everything other than the 'a'
.
Upvotes: 0
Views: 42
Reputation: 69944
Turns out that the "%1" bit has to do with string captures, not of substitution captures. I can solve the problem by adding a capture only around the parts that I want to replace, and I can leave the other parts alone.
local re = require "re"
local pattern = re.compile([[
line <- {~ (quoted / 'a'->'' / .)* ~}
quoted <- '"' rest
rest <- '"' / . rest
]])
Pay attention to operator precedence, because '->' binds tightly
('a' 'b')->''
Upvotes: 1
Reputation: 4637
Yes, you need substitution captures. You don't need re
, though:
-- Localisations for syntactic sugar and performance:
local lpeg = require 'lpeg'
local P, Cs = lpeg.P, lpeg.Cs
local any = P(1)
-- A string quoted with quote1 and quote2 (also quote1 if not set):
local function quoted (quote1, quote2)
return P (quote1) * (any - P (quote2 or quote1)) ^ 0 * P (quote2 or quote1)
end
-- The whole grammar. tbl is the replacement table:
local function replace_unquoted (tbl)
return Cs ((quoted'"' + any / tbl) ^ 0) * -1
end
print (replace_unquoted { a = 'o' }:match 'abcd "abcd" abcd')
Upvotes: 0