我正在尝试编写一个语法,但我遇到了一个问题,我将其简化为算术表达式的经典例子。我的.y文件的相关部分是:
%left '+' '-' %left '*' %left UNARY %token UNARY %token INTEGER %% start: expr {printf("Result: %d\n",$1);} expr : INTEGER | '-' expr %prec UNARY {$$ = -$2;} | expr '+' expr {$$ = $1 + $3;} | expr '-' expr {$$ = $1 - $3;} | expr '*' expr {$$ = $1 * $3;} | '(' expr ')' {$$ = $2;} ; %%
这个语法接受“1 + -2”和“1 + - 2”这样的表达式,但我想排除连续的运算符。是否有一种简单的方法可以在不使语法太复杂或者采用复杂的堆栈操作的情况下进行操作?
答案 0 :(得分:1)
通常的方法是将一元运算符拆分为单独的生产:
%left '+' '-'
%left '*'
%nonassoc UNARY
%token INTEGER
%%
start: expr {printf("Result: %d\n",$1);}
expr : primary
| '-' primary %prec UNARY {$$ = -$2;}
| expr '+' expr {$$ = $1 + $3;}
| expr '-' expr {$$ = $1 - $3;}
| expr '*' expr {$$ = $1 * $3;}
;
primary : INTEGER
| '(' expr ')' {$$ = $2;}
;
这是(部分)将优先规则转换为' normal'制作,然后调整它们以获得你想要的效果。
Alterantely,如果你想禁止所有连续的运算符,而不是只重复相同的运算符,你可以将前缀规则分解出去,这样它就不会出现在任何其他运算符之后:
%left '+' '-'
%left '*'
%nonassoc UNARY
%token INTEGER
%%
start: expr {printf("Result: %d\n",$1);}
expr : INTEGER
| '-' non_unary_expr %prec UNARY {$$ = -$2;}
| expr '+' non_unary_expr {$$ = $1 + $3;}
| expr '-' non_unary_expr {$$ = $1 - $3;}
| expr '*' non_unary_expr {$$ = $1 * $3;}
| '(' expr ')' {$$ = $2;}
;
non_unary_expr : INTEGER
| non_unary_expr '*' non_unary_expr {$$ = $1 * $3;}
| '(' expr ')' {$$ = $2;}
;
您可能会对最后一条规则中缺少二进制文件+
和-
的原因感到好奇 - 事实证明它们因优先级而不再需要(如果您添加它们,则可以使用#39;}我会得到一个关于他们因冲突而无用的警告。通常,当像这样重构时,我更喜欢摆脱所有优先级定义,而是使用单独的优先级规则,因为这会使事情更加清晰:
%token INTEGER
%%
start : expr { printf("Result: %d\n",$1); }
expr : term
| expr '+' non_unary_term { $$ = $1 + $2; }
| expr '-' non_unary_term { $$ = $1 - $2; }
;
term : factor
| term '*' non_unary_factor { $$ = $1 * $3; }
;
non_unary_term : non_unary_factor
| non_unary_term '*' non_unary_factor { $$ = $1 * $3; }
;
factor : INTEGER
| '-' non_unary_factor { $$ = -$2; }
| '(' expr ')' { $$ = $2; }
;
non_unary_factor : INTEGER
| '(' expr ')' { $$ = $2; }
;