Я пытаюсь использовать Menhir для написания синтаксического анализатора языка регулярных выражений. Моя желаемая грамматика, прежде чем я изменю ее, чтобы устранить двусмысленность, выглядит примерно так, как показано в следующем примере. Обратите внимание, что «упорядочивание/объединение» является неявным, и с этой операцией не связан токен.
%token LPAREN RPAREN
%token CHAR STAR PIPE
%token EOF
%start <unit> parse
%%
parse: re EOF {()}
re:
| LPAREN re RPAREN {()} (* Grouping *)
| CHAR {()} (* Single character *)
| re STAR {()} (* Kleene star *)
| re re {()} (* Sequencing / Concatenation *)
| re PIPE re {()} (* Alternation *)
Если бы у меня был токен для конкатенации, я мог бы устранить двусмысленность, просто используя объявления приоритета.
%token LPAREN RPAREN
%token CHAR STAR PIPE
%token CONCAT
%token EOF
%left PIPE
%left CONCAT
%nonassoc STAR
%start <unit> parse
%%
parse: re EOF {()}
re:
| LPAREN re RPAREN {()} (* Grouping *)
| CHAR {()} (* Single character *)
| re STAR {()} (* Kleene star *)
| re CONCAT re {()} (* Sequencing / Concatenation *)
| re PIPE re {()} (* Alternation *)
Однако я не могу заставить все работать без токена CONCAT в правиле конкатенации. Я попытался использовать объявление %prec
, но все еще оставались некоторые конфликты сдвига/уменьшения.
%token LPAREN RPAREN
%token CHAR STAR PIPE
%token CONCAT
%token EOF
%left PIPE
%left CONCAT
%nonassoc STAR
%start <unit> parse
%%
parse: re EOF {()}
re:
| LPAREN re RPAREN {()} (* Grouping *)
| CHAR {()} (* Single character *)
| re STAR {()} (* Kleene star *)
| re re %prec CONCAT {()} (* Sequencing / Concatenation *)
| re PIPE re {()} (* Alternation *)
Я думаю, это может быть потому, что менгир не может сказать, что последовательность должна быть левоассоциативной, но я не уверен на 100%, что это является причиной проблемы.
До сих пор единственное решение, которое я смог найти, заключалось в том, чтобы разбить правило re
на набор различных правил, которые делают уровни приоритета и ассоциативности явными:
%token LPAREN RPAREN
%token CHAR STAR PIPE
%token EOF
%start <unit> parse
%%
parse: re EOF {()}
re: re3 {()}
re0:
| LPAREN re RPAREN {()} (* Grouping *)
| CHAR {()} (* Single character *)
re1:
| re0 {()}
| re0 STAR {()} (* Kleene star *)
re2:
| re1 {()}
| re2 re1 {()} (* Sequencing / Concatenation *)
re3:
| re2 {()}
| re3 PIPE re2 {()} (* Alternation *)
Хотя этот последний пример работает нормально, мне действительно любопытно, можно ли удалить все неясности и конфликты, просто используя объявления приоритета и ассоциативности, без необходимости переписывать грамматику.