Сопоставить повторяемую строку как «целое слово» в Lua 5.1 - PullRequest
0 голосов
/ 04 февраля 2019

Моя среда:

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

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

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

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

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

  • input: " one !tvtvtv! two", word: 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 ]

0 голосов
/ 04 февраля 2019

Паттерны 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
0 голосов
/ 04 февраля 2019

Я думаю, что шаблон не работает надежно, потому что часть %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
...