Подтвердить что ты не робот

Почему Parsimonious отклоняет мой ввод с помощью IncompleteParseError?

Я пытаюсь разработать основной скелет для языка, который я разрабатывал, и я пытаюсь использовать Parsimonious сделать синтаксический анализ для меня. На данный момент я провозгласил следующую грамматику:

grammar = Grammar(
    """
    program = expr*
    expr    = _ "{" lvalue (rvalue / expr)* "}" _
    lvalue  = _ ~"[a-z0-9\\-]+" _
    rvalue  = _ ~".+" _
    _       = ~"[\\n\\s]*"
    """
)

Когда я пытаюсь вывести полученный AST простой строки ввода, например "{ do-something some-argument }":

print(grammar.parse("{ do-something some-argument }"))

Parsimonious решает отказаться от него, а затем дает мне эту довольно загадочную ошибку:

Traceback (most recent call last):
  File "tests.py", line 13, in <module>
    print(grammar.parse("{ do-something some-argument }"))
  File "/usr/local/lib/python2.7/dist-packages/parsimonious/grammar.py", line 112, in parse
    return self.default_rule.parse(text, pos=pos)
  File "/usr/local/lib/python2.7/dist-packages/parsimonious/expressions.py", line 109, in parse
    raise IncompleteParseError(text, node.end, self)
parsimonious.exceptions.IncompleteParseError: Rule 'program' matched in its entirety, but it didn't consume all the text. The non-matching portion of the text begins with '{ do-something some-' (line 1, column 1).

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

Я пробовал поиск в Интернете, но все, что я нашел, похоже, удаленно связано с этим вопросом, что никоим образом не помогло мне.

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

4b9b3361

Ответ 1

Я очень далек от эксперта по Parsimonious, но я считаю, что проблема заключается в том, что ~".+" жадно сопоставляет весь остаток входной строки, не оставляя ничего, чтобы соответствовать остальной части производства. Я сначала протестировал эту идею, изменив регулярное выражение на rvalue на ~"[a-z0-9\\-]+", то же самое, что и у lvalue. Теперь он анализирует и (удивительно) различает контекст между двумя одинаково определенными токенами lvalue и rvalue.

from parsimonious.grammar import Grammar

grammar = Grammar(
    """
    program = expr*
    expr    = _ "{" lvalue (rvalue / expr)* "}" _
    lvalue  = _ ~"[a-z0-9\\-]+" _
    rvalue  = _ ~"[a-z0-9\\-]+" _
    _       = ~"[\\n\\s]*"
    """
)

print(grammar.parse( "{ do-something some-argument }"))

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

rvalue = _ ~"[^\\s\\n]+" _

Но крики!

{ foo bar }

"}" является закрывающей фигурной скобкой, но также является последовательностью одного или нескольких символов без пробелов. Это "}" или rvalue? Грамматика говорит, что следующим токеном может быть любой из них. Одна из этих интерпретаций является сильной, а другая - нет, но Parsimonious просто говорит, что это шпинат и ад. Я не знаю, рассмотрит ли синтаксический анализ maven, что законный способ разрешить двусмысленность (например, может быть, такая грамматика может привести к случаям с двумя возможными интерпретациями, которые анализируются) или насколько практично это реализовать. В любом случае Parsimonious не делает этого звонка.

Итак, нам нужно отталкивать границы по фигуре. Я думаю, что эта грамматика делает то, что вы хотите:

from parsimonious.grammar import Grammar

grammar = Grammar(
    """
    program = expr*
    expr    = _ "{" lvalue (expr / rvalue)* "}" _
    lvalue  = _ ~"[a-z0-9\\-]+" _
    rvalue  = _ ~"[^{}\\n\\s]+" _
    _       = ~"[\\n\\s]*"
    """
)

print(grammar.match( "{ do-something some-argument 23423 {foo bar} &^%$ }"))

Я также исключил открытую фигурную скобку, потому что как бы вы ожидали, что эта строка будет tokenize?

{foo bar{baz poo}}

Я ожидал бы

"{" "foo" "bar" "{" "baz" "poo" "}" "}"

... потому что если "poo}" ожидается tokenize как "poo" "}", а "{foo" ожидается tokenize как "{" "foo", тогда обработка bar{baz как "bar{baz" или "bar{" "baz" - это невменяемый противоречивый.

Теперь я помню, как моя горькая ненависть к Якку привела меня к одержимости этим.