Сопоставить повторяемую строку как "целое слово" в Lua 5.1

Моя среда:

  • Lua 5.1
  • Абсолютно нельзя использовать библиотеки с собственным компонентом (например, C .so/.dll).
  • Я могу запустить любой произвольный чистый код Lua 5.1, но не могу получить доступ os и несколько других пакетов, которые позволили бы получить доступ к собственной файловой системе, командам оболочки или чему-либо подобному, поэтому все функциональные возможности должны быть реализованы в самом Lua (только).
  • Мне уже удалось вытащить LuLpeg. Я, вероятно, могу использовать другие чистые библиотеки Lua.

Мне нужно написать функцию, которая возвращает true если входная строка соответствует произвольной последовательности букв и цифр как целого слова, которое повторяется один или несколько раз и может иметь пунктуацию в начале или конце всей соответствующей подстроки. Я использую "целое слово" в том же смысле, что и граница слова PCRE \b,

Чтобы продемонстрировать идею, вот неправильная попытка с использованием re модуль LuLpeg; кажется, что он работает с негативными взглядами, но не с негативными взглядами:

function containsRepeatingWholeWord(input, word)
    return re.match(input:gsub('[%a%p]+', ' %1 '), '%s*[^%s]^0{"' .. word .. '"}+[^%s]^0%s*') ~= nil
end

Вот примеры строк и ожидаемое возвращаемое значение (кавычки синтаксические, как если бы они были напечатаны в интерпретаторе Lua, а не литеральные части строки; это сделано для того, чтобы сделать пробелы в конце / в начале очевидными):

  • вход: " one !tvtvtv! two", слово: tv, возвращаемое значение: true
  • вход: "I'd", слово: d, возвращаемое значение: false
  • вход: "tv", слово: tv, возвращаемое значение: true
  • вход: " tvtv! ", слово: tv, возвращаемое значение: true
  • вход: " epon ", слово: nope, возвращаемое значение: false
  • вход: " eponnope ", слово: nope, возвращаемое значение: false
  • вход: "atv", слово: tv, возвращаемое значение: false

Если бы у меня была полная библиотека регулярных выражений PCRE, я мог бы сделать это быстро, но я этого не делаю, потому что не могу связаться с C, и я не нашел ни одной чистой реализации Lua PCRE или подобной.

Я не уверен, достаточно ли гибок LPEG (используя LPEG напрямую или через re модуль), чтобы делать то, что я хочу, но я уверен, что встроенные функции Lua не могут делать то, что я хочу, потому что он не может обрабатывать повторяющиеся последовательности символов. (tv)+ не работает со встроенным Lua string:match функция и тому подобное.

Интересные ресурсы, которые я искал, чтобы попытаться выяснить, как это сделать, но безрезультатно:

2 ответа

Решение

Шаблоны Lua достаточно мощные.
LPEG здесь не нужен.

Это твоя функция

function f(input, word)
   return (" "..input:gsub(word:gsub("%%", "%%%%"), "\0").." "):find"%s%p*%z+%p*%s" ~= nil
end

Это проверка функции

for _, t in ipairs{
   {input = " one !tvtvtv! two", word = "tv", return_value = true},
   {input = "I'd", word = "d", return_value = false},
   {input = "tv", word = "tv", return_value = true},
   {input = "   tvtv!  ", word = "tv", return_value = true},
   {input = " epon ", word = "nope", return_value = false},
   {input = " eponnope ", word = "nope", return_value = false},
   {input = "atv", word = "tv", return_value = false},
} do
   assert(f(t.input, t.word) == t.return_value)
end

Я думаю, что шаблон не работает надежно, потому что %s*[^%s]^0 part соответствует необязательному ряду пробельных символов, за которыми следуют непробельные символы, и затем он пытается найти совпадение с повторяющимся словом и завершается неудачно. После этого он не перемещается назад или вперед в строке и пытается сопоставить дублированное слово в другой позиции. Семантика 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
Другие вопросы по тегам