Шаблон LPeg, который соответствует строкам без последовательных переносов

Я пытаюсь написать шаблон LPeg для соответствия строк, которые:

  • начать с буквы
  • после этого содержат буквенно-цифровые символы
  • не содержит двух или более последовательных дефисов (например, запрещает test--string)

Для справки, регулярное выражение [a-zA-Z](-?[a-zA-Z0-9])* соответствует тому, что я ищу.

Вот код, с которым я работаю, для справки:

require "lpeg"
P,R,C = lpeg.P,lpeg.R,lpeg.C

dash  = P"-"
ucase  = R"AZ"
lcase  = R"az"
digit  = R"09"
letter = ucase + lcase
alphanum = letter + digit

str_match = C(letter * ((dash^-1) * alphanum)^0)

strs = {
    "1too",
    "too0",
    "t-t-t",
    "t-t--t",
    "t--t-t",
    "t-1-t",
    "t--t",
    "t-one1",
    "1-1",
    "t-1",
    "t",
    "tt",
    "t1",
    "1",
}

for _,v in ipairs(strs) do
    if lpeg.match(str_match,v) ~= nil then
        print(v," => match!")
    else
        print(v," => no match")
    end
end

Однако, к моему большому разочарованию, я получаю следующий вывод:

1too     => no match
too0     => match!
t-t-t    => match!
t-t--t   => match!
t--t-t   => match!
t-1-t    => match!
t--t     => match!
t-one1   => match!
1-1      => no match
t-1      => match!
t        => match!
tt       => match!
t1       => match!
1        => no match

Несмотря на то, что код выводит, t-t--t, t--t-t, а также t--t не должно совпадать.

1 ответ

Решение

По твоему образцу letter * ((dash^-1) * alphanum)^0, lpeg попытается сопоставить префикс строки. Для случаев, когда вы не ожидали совпадения

тт - т
т - тт
т - т

Часть, выделенная жирным шрифтом, - это место, где ваш шаблон успешно совпадает. lpeg.match возвращает последнюю позицию (которая является числом), которую он смог проанализировать до использования вашего шаблона, если ничего не получено. Для вышеупомянутых 3 случаев захватывается соответствующая часть, которая объясняет ошибочный вывод, который вы видите.

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

str_match = C(letter * ((dash^-1) * alphanum)^0) * -1

Аналогично используя lpeg.re модуль

re_pat = re.compile "{ %a ('-'? %w)* } !."

Для сопоставления потока или поиска всех вхождений паттернов в целевой строке сложите правила грамматики вместе следующим образом

stream_parse = re.compile
[[
  stream_match  <- ((str_match / skip_nonmatch) delim)* str_match?
  str_match     <- { %a ('-'? %w)* } (&delim / !.)
  skip_nonmatch <- !str_match (!delim .)*

  delim         <- %s+
]]

Любые совпадения будут захвачены и возвращены. Если нет совпадений, вы либо вернетесь nil или число, указывающее, где в строке прервался анализ шаблона.

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

stream_parse = re.compile
[[
  stream_match  <- (str_match / skip_nonmatch+ &str_match)+
  str_match     <- { %a ('-'? %w)* } (&delim / !.)
  skip_nonmatch <- !str_match (!delim .)* delim

  delim         <- %s+
]]
Другие вопросы по тегам