У меня есть большая грамматика в PLY (Python Lexx Yacc) для языка, у которого есть определенные проблемы при разборе. Язык позволяет ведущему синтаксису двух видов вызовов выглядеть одинаково почти до конца нетерминального вызова. Это дает много возможностей для уменьшения/уменьшения конфликтов, поскольку семантика токенов на этом пути различна, но может быть построена из одних и тех же терминальных токенов. Я извлек простые версии до/после грамматики ниже, которые я немного объясню.
Первоначально выражения представляли собой типичную «слоистую грамматику», в которой вызовы и литералы и т. д. принимались за первичные, затем первичные через унарные, затем двоичные к общему выражению. Проблема заключалась в том, что Call_expr
с двумя аргументами конфликтует с версией Iter_expr
, которая начинается с двух идентификаторов перед '/'. Конфликт возник из-за запятой после первого аргумента в вызове, поскольку изначально было разрешено Expr -> ... -> Primary_expr -> Name_expr -> Id
. Парсер может либо уменьшить Id
до Expr
для соответствия Call_expr
, либо оставить его для соответствия Iter_expr
. Заглядывание вперед к запятой не помогло ему решить. Если первым аргументом вызова является просто идентификатор (например, переменная), это допустимая двусмысленность. Рассмотрим ввод id > id ( id , id ...
.
Мой подход заключался в том, чтобы сделать выражение, которое не могло бы быть просто Id
. Я добавил производственную цепочку через все выражения, чтобы дать им «_nn
» версий — «не имя». Затем я мог бы определить производство для Call_expr
, которое использует любой синтаксис в первом аргументе, что делает его больше, чем просто имя (например, операторы, вызовы и т. д.), чтобы уменьшить его до BinOp_expr_nn
, а также также разрешить вызов продукция, которая имеет только Id
в качестве первого аргумента. Это должно убедить синтаксический анализатор просто смещаться до тех пор, пока он не сможет разрешить либо Iter_expr
, либо Call_expr
(или, по крайней мере, узнать, на каком пути он находится).
Как вы могли догадаться, это все портит :). Работа с цепочкой выражений также связана с Primary_expr
, который мне все еще нужно разрешить уменьшить до Id
. Но теперь это конфликт уменьшения/уменьшения — каждый Primary_expr
может остаться там или перейти к Unary_expr
. Я могу приказать им сделать выбор (что может сработать), но я ожидаю, что в конечном итоге буду гоняться за другим и еще.
Итак, мой вопрос: есть ли метод, который кто-то может сформулировать о том, как разрешить одним и тем же токенам представлять различную семантику (т.е. expr против id), которые все еще можно анализировать с помощью LALR (1), например PLY? Если не считать этого, какие-нибудь полезные лайфхаки, которые помогут справиться с проблемой? Можно ли это обозначить?
terminals: '+' '^' ',' '>' '(' ')' '/' ':' 'id' 'literal'
(i.e. punctuation (besides '->' and '|', initial-lower-case words)
non-terminals: initial-Upper-case words
Оригинальная грамматика:
S'-> S
S -> Call_expr
| Iter_expr
Expr -> BinOp_expr
BinOp_expr -> Unary_expr
BinOp_expr -> BinOp_expr '+' BinOp_expr
Unary_expr -> Primary_expr
| '^' BinOp_expr
Primary_expr -> Name_expr
| Call_expr
| Iter_expr
| Literal_expr
Name_expr -> Id
Args -> Expr
| Args ',' Expr
Call_expr -> Primary_expr '>' Id '(' ')'
| Primary_expr '>' Id '(' Args ')'
Iter_expr -> Primary_expr '>' Id '(' Id '/' Expr ')'
| Primary_expr '>' Id '(' Id ':' Id '/' Expr ')'
| Primary_expr '>' Id '(' Id ',' Id ':' Id '/' Expr ')'
Literal_expr -> literal
Id -> id
Моя попытка устранить неоднозначность первого аргумента в Call_expr
:
S'-> S
S -> Call_expr
| Iter_expr
Expr -> BinOp_expr_nn
| BinOp_expr
BinOp_expr -> BinOp_expr_nn
| Unary_expr
BinOp_expr_nn -> Unary_expr_nn
| BinOp_expr '+' BinOp_expr
Unary_expr -> Primary_expr
| Unary_expr_nn
Unary_expr_nn -> Primary_expr_nn
| '^' BinOp_expr
Primary_expr -> Primary_expr_nn
| Name_expr
Primary_expr_nn -> Call_expr
| Iter_expr
| Literal_expr
Name_expr -> Id
Args -> Expr
| Args ',' Expr
Call_expr -> Primary_expr '>' Id '(' ')'
| Primary_expr '>' Id '(' Expr ')'
| Primary_expr '>' Id '(' Id , Args ')'
| Primary_expr '>' Id '(' BinOp_expr_nn , Args ')'
Iter_expr -> Primary_expr '>' Id '(' Id '/' Expr ')'
| Primary_expr '>' Id '(' Id ':' Id '/' Expr ')'
| Primary_expr '>' Id '(' Id ',' Id ':' Id '/' Expr ')'
Literal_expr -> literal
Id -> id
BinOp_expr -> Unary_expr | BinOp_expr '+' Unary_expr
(чтобы+
было менее ассоциативным; как написано, это неоднозначно) иUnary_expr -> Primary_expr | '^' Unary_expr
, чтобы^ a + b
означало(^a) + b
, а не^(a + b)
). (Или, возможно,Unary_expr -> Primary_expr | '^' Primary_expr
, если^^a
не разрешено.) - person rici   schedule 29.04.2018Primary_expr -> '(' Expr ')'
, чтобы разрешить скобки. - person rici   schedule 29.04.2018