YACC规则未被归约,出现无用非终结符与无用规则错误求解
YACC中「无用非终结符」与「无用规则」问题排查
问题描述
正在学习使用YACC,遇到了「无用非终结符」和「无用规则」两类错误。查阅资料得知这类错误通常由规则不可达导致,但反复检查后仍未找到问题,看起来所有规则的非终结符都出现在产生式两侧。以下是YACC代码及终端报错信息:
YACC代码
%{ #include <stdio.h> #include <string.h> #include <stdlib.h> extern int nLineas; void yyerror(const char* msg) { fprintf(stderr, "%s\n", msg); } int yylex(void); FILE *yyin; %} %start programa %union { int tipo; char nombre[30]; } %token <tipo> NUM %token CARACTER %token CADENA %token <nombre> ID %token INT %token DOUBLE %token CHAR %token FLOAT %token IF %token MAIN %token ELSE %token SCANF %token PRINTF %token DEFINE %token INCLUDE %token FOR %token WHILE %token DO %token IGUAL %token DIFERENTE %token MAYOR %token MENOR %token ESIGUAL %token ESDIFERENTE %token MAYORIGUAL %token MENORIGUAL %token '(' ')' %left '+' '-' %left '*' '/' '<' '>' //%left ">=" "<=" "==" "!=" "&&" "||" "!" %left UNARIO %% //PROGRAMA programa: DECLARACIONCONSTANTES DECLARACIONVARIABLES FUNCIONMAIN {printf("\nTodo correcto numero de lineas %d.\n",nLineas);} ; //SECCIONES DEL PROGRAMA //Declaracion de constantes DECLARACIONCONSTANTES: /*Vacia*/ |DECLARACIONCONSTANTE DECLARACIONCONSTANTES ; DECLARACIONCONSTANTE: '#' DEFINE ID NUM {printf("\nDeclaracion constante númerica.\n\n");} |'#' DEFINE ID '"' CADENA '"' {printf("\nDeclaracion constante cadena.\n\n");} //'#' DEFINE ID "'" "'" {printf("Declaracion constante cadena.\n");} Ver como hacer letra sin que lo incluyan en otro tipo ; //Declaracion de variables DECLARACIONVARIABLES: /*Vacia*/ |DECLARACIONVARIABLE DECLARACIONVARIABLES ; DECLARACIONVARIABLE: INT ID ';' {printf("\nDeclaracion variable int.\n\n");} |FLOAT ID ';' {printf("\nDeclaracion de variable float.\n\n");} |CHAR ID ';' {printf("\nDeclaracion de variable char.\n\n");} |CHAR ID '[' NUM ']' ';' {printf("\nDeclaracion de variable cadena de caracteres.\n\n");} ; //Funcion Main FUNCIONMAIN: CABECERAMAIN '{' DECLARACIONVARIABLES INSTRUCCIONES '}' ; CABECERAMAIN: MAIN '(' ')' {printf("\nCabecera main.\n\n");} ; INSTRUCCIONES: /*Vacio*/ |INSTRUCCION INSTRUCCIONES ; INSTRUCCION: DECLARACIONVARIABLES |ID '+' '+' ';' {printf("\nInstruccion -> incremento.\n\n");} |ID '-' '-' ';' {printf("\nInstruccion -> decremento.\n\n");} |ASIGNACION |VISUALIZACION |CONDICIONAL |MIENTRAS |LECTURA |EXPRESION ; CONDICIONAL: IF '(' EXPRESION ')' '{' INSTRUCCIONES '}' {printf("\nInstruccion condicional -> estructura if.\n\n");} |IF '(' EXPRESION ')' '{' INSTRUCCIONES '}' ELSE '{' INSTRUCCIONES '}' {printf("\nInstruccion condicional -> estructura if/else.\n\n");} ; MIENTRAS: WHILE '(' EXPRESION ')' '{' INSTRUCCIONES '}' {printf("\nInstruccion condicional mientras -> estructura while.\n\n");} ASIGNACION: ID IGUAL NUM ';' {printf("\nInstrucción de asignacion: idVariable ->expresion numerica.\n\n");} |ID IGUAL CADENA ';' {printf("\nInstrucción de asignación: idVariable -> cadena.\n\n");} //|ID '=' '"' '"' {printf("Instrucción de asignación: idVariable -> cadena\n");} Ver como hacer letra sin que lo incluyan en otro tipo ; VISUALIZACION: PRINTF '(' ID ')' ';' {printf("\nInstruccion de visualizacion -> printf de una variable.\n\n");} |PRINTF '(' CADENA ')' ';' {printf("\nInstruccion de visualizacion -> printf de una cadena.\n\n");} ; LECTURA: SCANF '(' VARIABLE ')' ';' {printf("\nInstruccion de lectura -> scanf.\n\n");} EXPRESION: VARIABLE OP_BOOLEANOS VARIABLE {printf("\nExpresion -> booleana.\n\n");} |VARIABLE OP_NUMERICOS VARIABLE {printf("\nExpresion -> numerica.\n\n");} ; VARIABLE: TERM VARIABLE ; TERM: NUM |CADENA |ID |OP_NUMERICOS |OP_BOOLEANOS |',' |'(' |')' ; OP_BOOLEANOS: '&&' |'||' |IGUAL |DIFERENTE |MAYOR |MENOR |ESIGUAL |ESDIFERENTE |MAYORIGUAL |MENORIGUAL ; OP_NUMERICOS: '+' |'-' |'*' |'/' ; %% int main() { yyin = fopen("Pruebas.c", "r"); if(yyin == NULL){ printf("ERROR de apertura de fichero.\n"); return 1; } yyparse(); return 0; }
终端报错信息
yacc.y: warning: 8 useless nonterminals and 33 useless rules yacc.y:93.1-11: warning: useless nonterminal: CONDICIONAL yacc.y:97.1-8: warning: useless nonterminal: MIENTRAS yacc.y:108.1-7: warning: useless nonterminal: LECTURA yacc.y:93.40-48: warning: useless nonterminal: EXPRESION yacc.y:108.43-50: warning: useless nonterminal: VARIABLE yacc.y:114.33-36: warning: useless nonterminal: TERM yacc.y:110.42-53: warning: useless nonterminal: OP_BOOLEANOS yacc.y:111.43-54: warning: useless nonterminal: OP_NUMERICOS yacc.y:93.33-135: warning: useless rule: CONDICIONAL: IF '(' EXPRESION ')' '{' INSTRUCCIONES '}' yacc.y:94.34-168: warning: useless rule: CONDICIONAL: IF '(' EXPRESION ')' '{' INSTRUCCIONES '}' ELSE '{' INSTRUCCIONES '}' yacc.y:97.33-150: warning: useless rule: MIENTRAS: WHILE '(' EXPRESION ')' '{' INSTRUCCIONES '}'
问题分析与修复
核心原因
报错的根源是**VARIABLE规则存在无限递归,无法推导到合法的终结符序列**:
VARIABLE: TERM VARIABLE ;
这个规则要求VARIABLE必须是TERM后紧跟另一个VARIABLE,没有终止条件,永远无法生成只包含终结符的串。导致依赖VARIABLE的EXPRESION无法被正确推导,进而所有依赖EXPRESION的规则(如CONDICIONAL、MIENTRAS)以及依赖VARIABLE的LECTURA都变成不可达,被标记为无用。
另外TERM规则中包含了运算符、括号、逗号等非变量元素,不符合语法逻辑,也会影响推导的合法性。
修复步骤
- 修正
VARIABLE规则,添加终止条件:
VARIABLE: TERM | TERM VARIABLE ;
这样VARIABLE可以是单个TERM,或者TERM后跟VARIABLE,能够正常终止递归推导。
- 调整
TERM规则,只保留合法的表达式单元:
TERM: NUM | CADENA | ID | '(' EXPRESION ')' // 支持括号包裹表达式,符合常规语法 ;
移除运算符、逗号等不属于表达式单元的元素,确保TERM代表合法的变量或值。
- 优化
EXPRESION规则(可选):
添加单个VARIABLE作为合法表达式的选项,让语法更健壮:
EXPRESION: VARIABLE OP_BOOLEANOS VARIABLE | VARIABLE OP_NUMERICOS VARIABLE | VARIABLE // 单个变量/值也是合法表达式 ;
完成以上修改后,重新编译YACC代码,「无用非终结符」和「无用规则」的警告会消失。
内容的提问来源于stack exchange,提问作者Péritas
相关产品推荐
相关产品推荐

