Это ожидаемое поведение.Pyparsing не делает никаких заглядываний вперед, но является чисто слева направо.Вы можете добавить в свой синтаксический анализ заглядывание вперед, но это то, что вы должны сделать для себя.
Вы можете получить более подробную информацию о том, что происходит, если включите отладку для a
и b
:
a.setName('a').setDebug()
b.setName('b').setDebug()
, который покажет вам, что каждый раз, когда pyparsing собирается соответствовать выражению, а затем, если совпадение не удалось или получилось успешно, и если оно прошло успешно, соответствующие токены:
Match a at loc 0(1,1)
Matched a -> ['hello', ';']
Match a at loc 6(1,7)
Exception raised:Expected W:(ABCD...) (at char 6), (line:1, col:7)
Match b at loc 6(1,7)
Exception raised:Expected W:(ABCD...) (at char 6), (line:1, col:7)
Поскольку a
соответствует полной входной строке, это соответствует критерию «ноль или более».Затем pyparsing переходит к совпадению b
, но так как слово и точка с запятой уже прочитаны, анализировать больше не нужно.Поскольку b
не является обязательным, pyparsing вызывает исключение, которое не может быть найдено.Даже если вы проанализируете "hello; hello; hello;", все строки и полуфабрикаты будут использованы ZeroOrMore без завершающего b
оставленного для анализа.
Попробуйте это:
not_so_bad_parser = ZeroOrMore(a + ~StringEnd()) + b
Утверждая, что вы хотите прочитать только a
выражения, которые не находятся в конце строки, а затем проанализировать "hello;"не будет соответствовать a
, и поэтому перейдите к b
, который затем соответствует.
Это настолько распространенная проблема, что я добавил ключевое слово stopOn
в конструкторы классов ZeroOrMore и OneOrMore, чтобы избежатьнужно добавить оверт ~
(имеется в виду NotAny).Сначала я подумал, что это может сработать:
even_less_bad_parser = ZeroOrMore(a, stopOn=b) + b
Но потом, поскольку b
также соответствует a
, это будет никогда совпадать с a
с, иможет оставить бесподобный текст.Нам нужно останавливаться на b
только в том случае, если в конце строки:
even_less_bad_parser = ZeroOrMore(a, stopOn=b + StringEnd()) + b
Я не уверен, действительно ли это удовлетворит вашу концепцию "менее плохого" состояния, но именно поэтомуpyparsing ведет себя так, как он для вас.