Я думаю, что шаблон не работает надежно, потому что часть %s*[^%s]^0
соответствует необязательному ряду пробельных символов, за которыми следуют непробельные символы, а затем пытается найти совпадение с повторяющимся словом и завершается неудачно.После этого он не перемещается назад или вперед в строке и пытается сопоставить дублированное слово в другой позиции.Семантика LPeg и re
очень отличается от семантики большинства движков регулярных выражений, даже для вещей, которые похожи.
Вот версия на основе re
.У шаблона есть один захват (сокращенное слово), поэтому, если найденное дублированное слово найдено, сопоставление возвращает строку, а не число.
function f(str, word)
local patt = re.compile([[
match_global <- repeated / ( [%s%p] repeated / . )+
repeated <- { %word+ } (&[%s%p] / !.) ]],
{ word = word })
return type(patt:match(str)) == 'string'
end
Это несколько сложно, потому что ваниль re
не делаетесть способ создать шаблон lpeg.B
.
Вот версия lpeg
с использованием lpeg.B
.LuLPeg также работает здесь.
local lpeg = require 'lpeg'
lpeg.locale(lpeg)
local function is_at_beginning(_, pos)
return pos == 1
end
function find_reduplicated_word(str, word)
local type, _ENV = type, math
local B, C, Cmt, P, V = lpeg.B, lpeg.C, lpeg.Cmt, lpeg.P, lpeg.V
local non_word = lpeg.space + lpeg.punct
local patt = P {
(V 'repeated' + 1)^1,
repeated = (B(non_word) + Cmt(true, is_at_beginning))
* C(P(word)^1)
* #(non_word + P(-1))
}
return type(patt:match(str)) == 'string'
end
for _, test in ipairs {
{ 'tvtv', true },
{ ' tvtv', true },
{ ' !tv', true },
{ 'atv', false },
{ 'tva', false },
{ 'gun tv', true },
{ '!tv', true },
} do
local str, expected = table.unpack(test)
local result = find_reduplicated_word(str, 'tv')
if result ~= expected then
print(result)
print(('"%s" should%s match but did%s')
:format(str, expected and "" or "n't", expected and "n't" or ""))
end
end