问题描述
我正在使用 Bison 和 Flex 来尝试解析提供给我的简单语法。在这个语法中(几乎)一切都被认为是一个表达式并具有某种价值;没有声明。更重要的是,EBNF 的语法定义带有一定的歧义:
-
expression OP expression
其中 op 可以是“+”、“-”、“&”等。这可以使用 bison 的结合性运算符并根据通用语言标准设置 %left、%right 和 %nonassoc 轻松解决。 -
IF expression THEN expression [ELSE expression]
以及DO expression WHILE expression
,忽略常见的悬垂 else 问题,我想要以下行为:
在 if-then-else 和 while 表达式中,嵌入的表达式尽可能长(语法允许)。例如 5 + if cond_expr then then_expr else 10 + 12
等价于 5 + (if cond_expr then then_expr else (10 + 12))
而 not 5 + (if cond_expr then then_expr else 10) + 12
鉴于语言中的所有内容都被视为表达式,我无法找到一种方法以不引起冲突的形式重新编写产生式规则。我尝试过的一件事是,从野牛手册中悬挂的 else 示例中汲取灵感:
expression: long_expression
| short_expression
;
long_expression: short_expression
| IF long_expression THEN long_expression
| IF long_expression long_expression ELSE long_expression
| WHILE long_expression DO long_expression
;
short_expression: short_expression '+' short_expression
| short_expression '-' short_expression
...
;
但是这似乎不起作用,我不知道如何将其调整为工作。请注意,我(假设我)已经按照某些书中的建议对 ELSE 和 THEN 使用 nonassoc 以及上述构造解决了悬空的 ELSE 问题,但我不确定这在没有语句而只有表达式的情况下是否有效。请注意,已为所有其他运算符(例如 +、- 等)设置了关联性。是否有解决此问题的解决方案、提示或示例?
----------- 编辑:最小示例 ---------------
我尝试包含所有具有特定关联性的标记的产生式,包括一些额外的产生式以显示一些语法。请注意,我实际上并没有使用我上面解释的想法。还要注意,我包含了一个二元和一元运算符只是为了使代码更短一些,所有运算符的规则都是相同的形式。带有 -Wall 标志的 Bison 发现与这些声明没有冲突(但我很确定它们不是 100% 正确的)。
%token<int> INT32 LET IF WHILE INTEGER OBJECTID TYPEID NEW
%right <str> THEN ELSE STR
%right '^' UMINUS NOT ISNULL ASSIGN DO IN
%left '+' '-'
%left '*' '/'
%left <str> AND '.'
%nonassoc '<' '='
%nonassoc <str> LOWEREQ
%type<ast_expr> expression
%type ...
exprlist: expression { ; }
| exprlist ';' expression { ; };
block: '{' exprlist '}' { ; };
args: %empty { ; }
| expression { ; }
| args ',' expression { ; };
expression: IF expression THEN expression { ; }
| IF expression THEN expression ELSE expression { ; }
| WHILE expression DO expression { ; }
| LET OBJECTID ':' type IN expression { ; }
| NOT expression { /* UNARY OPERATORS */ ; }
| expression '=' expression { /* BINARY OPERATORS */ ; }
| OBJECTID '(' args ')' { ; }
| expression '.' OBJECTID '(' args ')' { ; }
| NEW TYPEID { ; }
| STR { ; }
| INTEGER { ; }
| '(' ')' { ; }
| '(' expression ')' { ; }
| block { ; }
;
解决方法
以下关联声明解决了所有移位/减少冲突并产生了预期的输出(至少在我能想到的所有测试中):
...
%right <str> THEN ELSE
%right DO IN
%right ASSIGN
%left <str> AND
%right NOT
%nonassoc '<' '=' LOWEREQ
%left '+' '-'
%left '*' '/'
%right UMINUS ISNULL
%right '^'
%left '.'
...
%%
...
expression: IF expression THEN expression
| IF expression THEN expression ELSE expression
| WHILE expression DO expression
| LET OBJECTID ':' type IN expression
| LET OBJECTID ':' type ASSIGN expression IN expression
| OBJECTID ASSIGN expression
...
| '-' expression %prec UMINUS
| expression '=' expression
...
| expression LOWEREQ expression
| OBJECTID '(' args ')'
...
...
请注意,所有符号的关联性声明顺序和优先规则很重要!我没有包括所有的产生式规则,但是 if-else-then、while-do、let in、一元和二元操作数是产生冲突或具有不同关联性声明的错误结果的规则。