GCC上古版本(3.4)还有yacc,学习GCC如何实现if else 嵌套的问题。即:
问题
else后面的if到底是else if语义
if (xxx)a=1
else if (xxx) a=2
还是 else (语法块中的if else)。
if (xxx)a=1
else if (xxx) a = 2 else a=2;
PostgreSQL的PLpgSQL中的if else
PostgreSQL中因为没有else if语法,只有elif,所以语法规则实现比较简单,没有dangling else的问题。
stmt_if : K_IF expr_until_then proc_sect stmt_elsifs stmt_else K_END K_IF ';'{PLpgSQL_stmt_if *new;new = palloc0(sizeof(PLpgSQL_stmt_if));new->cmd_type = PLPGSQL_STMT_IF;new->lineno = plpgsql_location_to_lineno(@1);new->stmtid = ++plpgsql_curr_compile->nstatements;new->cond = $2;new->then_body = $3;new->elsif_list = $4;new->else_body = $5;$$ = (PLpgSQL_stmt *)new;};stmt_elsifs :{$$ = NIL;}| stmt_elsifs K_ELSIF expr_until_then proc_sect{PLpgSQL_if_elsif *new;new = palloc0(sizeof(PLpgSQL_if_elsif));new->lineno = plpgsql_location_to_lineno(@2);new->cond = $3;new->stmts = $4;$$ = lappend($1, new);};stmt_else :{$$ = NIL;}| K_ELSE proc_sect{$$ = $2;};
GCC的解法
那么C语言中支持else if的语法:
c-parse
%token IF ELSE%nonassoc IF
%nonassoc ELSEselect_or_iter_stmt:simple_if ELSE{ c_expand_start_else (); $<itype>1 = stmt_count; }c99_block_lineno_labeled_stmt{ c_finish_else ();c_expand_end_cond ();if (extra_warnings && stmt_count == $<itype>1)warning ("empty body in an else-statement"); }| simple_if %prec IF{c_expand_end_cond ();if (extra_warnings && stmt_count++ == $<itype>1)warning ("%Hempty body in an if-statement",&if_stmt_locus); }| simple_if ELSE error{ c_expand_end_cond (); }
注意递归部分是simple_if:
- c99_block_lineno_labeled_stmt代表语法块,可以包含if else。
- 解决关键点:将simple_if非终结符的优先级降低到if,当出现else simple_if时,让simple_if去reduce。
- 解决关键点:else的优先级比if要高,当else if出现时,发生shift/reduce冲突,根据优先级if会选择reduce。
《使用优先级解决shift/reduce冲突的经典例子(%prec UMINUS)》
手册中相关部分
https://www.gnu.org/software/bison/manual/bison.html#Shift_002fReduce