Вы разрешаете неявное умножение. Это означает, что f(x)
может быть проанализировано как произведение, и в этом случае f
должно быть уменьшено до fourth
согласно правилу неявного умножения. Но если его нужно проанализировать как присвоение, его нужно оставить как NAME
. Это конфликт с уменьшением сдвига, который можно легко увидеть в parser.out
:
state 3
(16) statement -> NAME . = expression
(17) statement -> NAME . ( expression ) = expression
(18) first -> NAME .
! shift/reduce conflict for ( resolved as shift
Здесь вы видите, что когда анализатор видит NAME
, за которым следует (
, он не знает следует ли уменьшить NAME
до first
(а затем до second
и т. д. c. до fourth
) в ожидании, что оператор является простым вычислением, или сместить (
, тем самым взяв на себя обязательство рассматривать это как определение функции.
Возможно, вы уже сталкивались с подобной проблемой, поскольку естественная грамматика для определения функции:
statement : NAME '(' NAME ')' '=' expression
, но вы заменили второй NAME
с expression
. Это позволит избежать конфликта сдвига-уменьшения до )
за счет принятия сомнительных определений функций (f(a+3) = 2a
).
Можно сделать нечто подобное, чтобы избежать этого конфликта сдвига-уменьшения (но это очень ad ho c решение):
statement : fourth '(' expression ')' '=' expression
Это «работает» в том смысле, что оно принимает правильные выражения. Но он также молча (или несколько молча) принимает несколько других выражений:
Это хорошо:
>>> f(a) = a + 3
('a', 'a+3')
Но это странно:
>>> -f(a) = a + 3
('a', 'a+3')
>>> 3f(a) = a + 3
('a', 'a+3')
>>> 3f(a+2) = a + 3
('a+2', 'a+3')
В качестве альтернативы, вы можете игнорировать конфликт сдвиг-уменьшение, так как PLY по умолчанию выполнит сдвиг (как сказано в parser.out
: «разрешен как сдвиг»). Это не позволит парсеру принять странные примеры, приведенные выше, но также неправильно проанализирует некоторые выражения, которые могут показаться разумными:
Эти выражения кажутся подходящими:
>>> f(a) = a + 3
('a', 'a+3')
>>> -f(a) = a + 3
Syntax error!
a+3
>>> 3f(a) = a + 3
Syntax error!
a+3
Но мы могли бы ожидать этого напечатать 105:
>>> a=7
7
>>> a(2a+1)
Syntax error!
Если вас это не беспокоит, вы можете прекратить читать сейчас.
Ваша грамматика не является неоднозначной и не будет неоднозначной, если вы написал синтаксис определения с большей специфичностью:
statement : NAME '(' NAME ')' '=' expression
или, если вы хотите разрешить функции с несколькими аргументами:
statement : NAME '(' name_list ')' '=' expression
name_list : NAME
name_list : name_list ',' NAME
Но это не LR (1), а это будет очень трудно сделать LR (1). Оба предложенных выше синтаксиса - это LR (4), и, поскольку каждая грамматика LR (k) теоретически может быть механически преобразована в (очень раздутую и трудно читаемую) грамматику LR (1), решение должно существовать. Это не будет красиво, однако.
(Фактический синтаксис, который вы используете, с expression
, не является LR (k) для любого k, потому что expression
может быть произвольно длинной, а анализатор имеет чтобы увидеть за пределами списка аргументов, чтобы решить, следует ли уменьшить первую NAME
.)
Поскольку грамматика однозначна, вы можете проанализировать ее с помощью GLR / GLL / Earley / et c , синтаксический анализатор, но PLY не производит их, и я не знаю генератора синтаксического анализатора Python, который это делает (хотя это не означает, что он не существует). Существуют различные генераторы синтаксического анализатора GLR для других языков.
Однако с PLY лучше всего использовать общий синтаксис, показанный выше, в качестве ad ho c решения, а затем решить проблему принятия неверного определения, выполнив проверку в действии semanti c.
Однако эта проверка будет немного сложнее, если вы не укусите пулю и не перейдете на анализатор, который выдает AST вместо выполнения немедленной оценки, как мы обсуждали несколько раз. Если значения semanti c являются AST, то будет просто проверить, что p[1]
и p[3]
являются простыми именами в функции действия для
statement : fourth '(' expression ')' '=' expression
Полагаю, человеческая изобретательность будучи безграничным, вы можете найти другой взлом, который позволит вам сделать тест. Но не ожидайте особой симпатии, когда это не сработает на угловом корпусе.