likethevegetable
likethevegetable

Reputation: 304

lua lpeg expression to not sub in between delimeters

I would like to understand how I could lpeg to replace strings if they are NOT between a certain start and end delimiter. Below is an example, where I would like to use SKIPstart and SKIPstop to signify where text shouldn't be replaced.

rep
rep
SKIPstart
rep
rep
SKIPstop
rep
rep

to

new
new
SKIPstart
rep
rep
SKIPstop
new
new

Here would be another example with multiple delimiters:

rep
rep
SKIPstart
rep
rep
SKIPstop
rep
rep
SKIPstart
rep
rep
SKIPstop

to

new
new
SKIPstart
rep
rep
SKIPstop
new
new
SKIPstart
rep
rep
SKIPstop

and nested

rep
rep
SKIPstart
rep
SKIPstart
rep
SKIPstop
rep
SKIPstop
rep
rep

to

new
new
SKIPstart
rep
SKIPstart
rep
SKIPstop
rep
SKIPstop
new
new

Upvotes: 0

Views: 123

Answers (2)

Brynne Taylor
Brynne Taylor

Reputation: 116

Egor Skriptunoff's answer is a great way of playing tricks with standard lua patterns to achieve your goal. I agree that if a straightforward way can work, I won't recommend using LPeg or other external libraries.

As you asked about LPeg, I'll show you how you can do it with LPeg.

local re = require('lpeg.re')

local defs = {
  do_rep = function(p)
    return p:gsub('rep', 'new')
  end
}

local pat = re.compile([=[--lpeg
  all <- {~ ( (!delimited . [^S]*)+ -> do_rep / delimited )* ~}
  delimited <- s (!s !e . / delimited)* e
  s <- 'SKIPstart'
  e <- 'SKIPstop'
]=], defs)

local s = [[
rep
rep
SKIPstart
rep
rep
SKIPstop
rep
rep
SKIPstart
rep
SKIPstart
rep
SKIPstop
rep
SKIPstop
rep
rep
]]

s = pat:match(s)
print(s)

Upvotes: 0

Egor Skriptunoff
Egor Skriptunoff

Reputation: 23737

Sorry, I don't know lpeg, but your task is easily solvable with usual Lua patterns.
IMO, lpeg or other external regex libraries are overkill in most cases, Lua patterns are surprisingly good enough.

local s = [[
rep
rep
SKIPstart
rep
rep
SKIPstop
rep
rep
SKIPstart
rep
SKIPstart
rep
SKIPstop
rep
SKIPstop
rep
rep
]]
s = s:gsub("SKIPstart", "\1%0")
     :gsub("SKIPstop", "%0\2")
     :gsub("%b\1\2", "\0%0\0")
     :gsub("(%Z*)%z?(%Z*)%z?",
         function(a, b) return a:gsub("rep", "new")..b:gsub("[\1\2]", "") end)
print(s)

Output:

new
new
SKIPstart
rep
rep
SKIPstop
new
new
SKIPstart
rep
SKIPstart
rep
SKIPstop
rep
SKIPstop
new
new

Upvotes: 1

Related Questions