5
votes

I'm trying to learn how patterns (implemented in string.gmatch, etc.) do work in Lua 5.3, from the reference manual.

(Thanks @greatwolf for correcting my interpretation about the pattern item using *.)

What I'm trying to do is to match '(%(.*%))*' (substrings enclosed by ( and ); for example, '(grouped (etc))'), so that it logs

(grouped (etc))
(etc)

or

grouped (etc)
etc

But it does nothing ???? (online compiler).

local test = '(grouped (etc))'

for sub in test:gmatch '(%(.*%))*' do
    print(sub)
end
3
Why do you write *. instead of .*? - Egor Skriptunoff
@EgorSkriptunoff I've wanted to match everything that is between ( and ), and by the manual, this is a pattern item, where a character class must be following * (in this "following" term I understood, "declared after *"). I think .\* ends up using the next character class (in the question example, %, at %)), same as .*%, I guess. - Klaider
You're mistaken here, all those repetition characters *, -, + etc. comes after the char class you want to match. For example to match letters use %a*, .* will greedy match any character. Your example just uses * kleene star but nothing precedes it so it's not well-formed. - greatwolf
@greatwolf Thanks for the correction, English isn't my primary language, so I may noy read it properly. Though Portuguese is similiar sometimes! - Klaider
For your example, you could try %a+%s(%(.-%)), which means match 1 or more letters(case insensitive) followed by optional space followed by any characters in ( ). On match capture the stuff in the parenthesis. - greatwolf

3 Answers

2
votes

Another possibility -- using recursion:

function show(s)
  for s in s:gmatch '%b()' do
    print(s)
    show(s:sub(2,-2))
  end
end

show '(grouped (etc))'
1
votes

I don't think you can do this with gmatch but using %b() along with the while loop may work:

local pos, _, sub = 0
while true do
  pos, _, sub  = ('(grouped (etc))'):find('(%b())', pos+1)
  if not sub then break end
  print(sub)
end

This prints your expected results for me.

1
votes
local test = '(grouped (etc))'

print( test:match '.+%((.-)%)' )

Here:

. +%( catch the maximum number of characters until it %( ie until the last bracket including it, where %( just escapes the bracket.

(.-)%) will return your substring to the first escaped bracket %)