Lark, как описать серию необязательных токенов
Я анализирую файл в формате, который может включать в себя:
INT32 price min 10 max 100 alertIfSold ;
Токены min, max и alertIfSold являются необязательными и могут отображаться в любом порядке. То есть
INT32 price max 100 alertIfSold ;
INT32 price max 100 min 10 alertIfSold ;
INT32 price alertIfSold ;
INT32 price;
все действительные примеры.
Ниже приведена простая версия грамматики, которую я тестирую. при запуске python test.py сгенерируйте эту ошибку:
lark.common.ParseError: Обнаружена бесконечная рекурсия! (правило <__anon_star_1: __anon_star_1>)
Я пытался выразить те же дополнительные маркеры, используя другие правила грамматики, с похожими результатами (бесконечная рекурсия).
Какова правильная грамматика для выражения необязательных параметров?
#test.py
from lark import lark
simplified_grammar = """
start: line+
line: TYPE CNAME [MIN MAX ALERT]* ";" -> foo
TYPE: "INT32" | "INT64"
MIN: "min" /[0-9]+/
MAX: "max" /[0-9]+/
ALERT: "alertIfSold"
%import common.CNAME
%import common.WS
%ignore WS
"""
sample = """
INT32 price max 100 alertIfSold ;
INT32 price max 100 min 10 alertIfSold ;
INT32 price alertIfSold ;
INT32 price;
"""
parser = lark.Lark(simplified_grammar)
def main():
parse_tree = parser.parse(sample)
if __name__ == '__main__':
main()
1 ответ
Ты хочешь:
line: TYPE CNAME (MIN | MAX | ALERT)* ";" -> foo
(Заметка: ()
вместо []
.)
В синтаксисе EBARF жаворонка, [item]
означает "необязательный item
" а также item*
означает "любое число (возможно, ноль) item
". Так [item]*
означает "любое число (возможно, ноль) любого из item
или ничего ". Но" любое количество ничего "бесконечно неоднозначно, вы не можете сказать, сколько ничего в пустой строке.
Поскольку вы на самом деле не намерены требовать, чтобы пункты появлялись в строгой последовательности, вы могли подумать о
line: TYPE CNAME ([MIN] [MAX] [ALERT])* ";" -> foo
Это было бы более точным, но это также привело бы к тому же сообщению об ошибке. В общем, вы не можете использовать звезду Клини на обнуляемом подшаблоне. Некоторые генераторы EBNF исправят это, удалив ε из повторного набора (и затем сделав повторение в целом необязательным), но lark не является одним из них. В этом случае исправление тривиально, но есть и другие случаи, в которых оно более раздражает.
Как регулярные выражения, (a* b*)*
, (a? b?)*
а также (a|b)*
эквивалентны в том смысле, что они все признают один и тот же язык. Но регулярные выражения общеизвестно неоднозначны, и парсеры обычно предпочитают однозначные грамматики или, в худшем случае, конечно неоднозначные грамматики. В эту категорию попадает только последнее регулярное выражение, и именно эту форму вы обычно предпочитаете.