#lua #lpeg
#lua #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. Взгляните поближе на
((dash^-1) * alphanum)^0
! Вы говорите «Группируйте возможность не более одного тире, за которым следует ровно один буквенно-цифровой символ, и все это может повторяться 0 или более раз».. Таким образом, ваш шаблон может просто завершиться ошибкой в начальной букве.
Ответ №1:
В вашем шаблоне letter * ((dash^-1) * alphanum)^0
lpeg попытается сопоставить с префиксом строки. Для случаев, когда вы не ожидали совпадения
t-t—t
t—t-t
t—t
Часть, выделенная жирным шрифтом, — это место, где ваш шаблон успешно соответствует. 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)* } (amp;delim / !.)
skip_nonmatch <- !str_match (!delim .)*
delim <- %s
]]
Любые совпадения будут зафиксированы и возвращены. Если совпадений нет, вы либо получите ответ nil
, либо число, указывающее, где в строке шаблон прекратил синтаксический анализ.
Редактировать: В случаях, когда вам нужно, чтобы синтаксический анализ возвращался nil
при отсутствии совпадения, эта настройка грамматики должна сработать
stream_parse = re.compile
[[
stream_match <- (str_match / skip_nonmatch amp;str_match)
str_match <- { %a ('-'? %w)* } (amp;delim / !.)
skip_nonmatch <- !str_match (!delim .)* delim
delim <- %s
]]