Шаблон 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+
]]