Lark, как описать серию необязательных токенов - PullRequest
0 голосов
/ 05 июня 2018

Я анализирую файл в формате, который может включать в себя:

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 Ответ

0 голосов
/ 05 июня 2018

Вы хотите:

line:  TYPE  CNAME (MIN | MAX | ALERT)* ";"    -> foo

(Примечание: () вместо [].)

В синтаксисе EBNF жаворонка [item] означает «необязательный item»и item* означает «любое число (возможно, ноль) из item».Таким образом, [item]* означает «любое число (возможно, ноль) либо item, либо ничего».Но «любое число ничего» бесконечно неоднозначно;вы не можете сказать, сколько всего в пустой строке есть.

Поскольку вы на самом деле не намерены требовать, чтобы пункты появлялись в строгой последовательности, вы могли подумать о

line:  TYPE  CNAME ([MIN] [MAX] [ALERT])* ";"    -> foo

Это было бы более точно, но это также привело бы к тому же сообщению об ошибке.В общем, вы не можете использовать звезду Клини на обнуляемом подшаблоне.Некоторые генераторы EBNF исправят это, удалив ε из повторного набора (а затем сделав повторение в целом необязательным), но lark не является одним из них.В этом случае исправление тривиально, но есть и другие случаи, в которых оно более раздражает.

Как регулярные выражения, (a* b*)*, (a? b?)* и (a|b)* эквивалентны в том смысле, что все они распознаюттот же язык.Но регулярные выражения общеизвестно неоднозначны, и парсеры обычно предпочитают однозначные грамматики или, в худшем случае, конечно неоднозначные грамматики.Только последнее регулярное выражение попадает в эту категорию, и именно эту форму вы обычно предпочитаете.

...