微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

模棱两可的无上下文语法? / 转移/减少 CUP 中的冲突

如何解决模棱两可的无上下文语法? / 转移/减少 CUP 中的冲突

我有以下 C++ 简化版本的上下文无关语法。当我使用 jflex 和 CUP 运行它时,我得到了如下错误列表:

Warning : *** Reduce/Reduce conflict found in state #173
  between especificador ::= (*) 
  and     programa ::= (*) 
  under symbols: {VOID,CHAR,FLOAT,DOUBLE,SIGNED,UNSIGNED,INT,SHORT,LONG}
  Resolved in favor of the second production.

我认为问题出在 instrucoesIf 上,但我无法弄清楚

start with programa;

programa ::= especificador tipo ID programa2 | DEFINE ID num CRLF programa | ; verificar depois o ERRO
especificador ::= AUTO | STATIC | EXTERN | CONST | ;
tipo ::= VOID | CHAR | FLOAT | DOUBLE | SIGNED inteiro | UNSIGNED inteiro | inteiro;

inteiro ::= SHORT | INT | LONG;
programa2 ::= SEMICOLON programa | LBRACK num RBRACK SEMICOLON programa | LPAREN listaParametros RPAREN bloco programa | COMMA listaID programa;

listaID ::= ID declaracaoParam2 listaIDTail;
listaIDTail ::= SEMICOLON | COMMA listaID;
listaParametros ::= listaParamRestante | ;
listaParamRestante ::= declaracaoParam declParamRestante;
declaracaoParam ::= tipo ID declaracaoParam2;
declaracaoParam2 ::= LBRACK num RBRACK | ;
declParamRestante ::= COMMA listaParamRestante | ;
bloco ::= LBRACE conjuntoInst RBRACE | SEMICOLON conjuntoInst ;
conjuntoInst ::= programa conjuntoInst | instrucoes conjuntoInst | ;
instrucoes ::= ID expressao SEMICOLON | RETURN expr SEMICOLON | PRINTF LPAREN expr RPAREN SEMICOLON | SCANF LPAREN ID RPAREN SEMICOLON | BREAK SEMICOLON | IF LPAREN expr RPAREN instrucoes instrucoesIf;

instrucoesIf ::= ELSE instrucoes | ;
expressao ::= atribuicao | LBRACK expr RBRACK atribuicao | LPAREN exprList RPAREN | ;
atribuicao ::= operadorAtrib expr;
operadorAtrib ::= EQ | MULTEQ | diveQ | MODEQ | PLUSEQ | MINUSEQ;
expr ::= exprAnd exprOr;
exprList ::= expr exprListTail | ;
exprListTail ::= COMMA exprList | ;
exprOr ::= OR exprAnd exprOr | ;
exprAnd ::= exprEqual exprAnd2;
exprAnd2 ::= AND exprEqual exprAnd2 | ;
exprEqual ::= exprRelational exprEqual2;
exprEqual2 ::= EQEQ exprRelational exprEqual2 | NOTEQ exprRelational exprEqual2 | ;
exprRelational ::= exprPlus exprRelational2;
exprRelational2 ::= LT exprPlus exprRelational2 | LTEQ exprPlus exprRelational2 | GT exprPlus exprRelational2 | GTEQ exprPlus exprRelational2 | ;

exprPlus ::= exprMult exprPlus2;
exprPlus2 ::= PLUS exprMult exprPlus2 | MINUS exprMult exprPlus2 | ;
exprMult ::= exprUnary exprMult2;
exprMult2 ::= MULT exprUnary exprMult2 | DIV exprUnary exprMult2 | ;
exprUnary ::= PLUS exprParenthesis | MINUS exprParenthesis | exprParenthesis;
exprParenthesis ::= LPAREN expr RPAREN | primary;
primary ::= ID primaryID | num | literal;
primaryID ::= LBRACK primary RBRACK | LPAREN exprList RPAREN | ;

literal ::= STRING | CHAR;
num ::= NUM_INT | NUM_FLOAT;

解决方法

CUP 是一个 LALR 解析器生成器,这意味着不需要避免左递归,因此您可以使用更自然的语法风格;无需将列表拆分为“开始”和“继续”产生式,这些产生式难以阅读且容易出错,而且通常不允许直接构建准确的语法树。

你的语法有很多冲突,但这个显然是模棱两可的:

conjuntoInst ::= programa conjuntoInst | instrucoes conjuntoInst | ;

请注意,programa 也有一个空选项。因此,在 programa 的开头(或实际上,在中间)可能有任意数量的空 conjuntoInst。对于不能派生任何东西的非终端,您需要非常小心;您必须避免在未定界列表中使用它们,因为解析器无法判断源文本中存在多少个连续的空非终结符。

请注意,C++ 和 C 中都不允许完全空的语句。完全空的语句会产生这种歧义。空语句仍必须以 ; 结束。这使得拥有语句列表成为可能。

所以 C 语句(包括块)(省略了很多细节)的常用模型是:

block ::= '{' statementList '}' ;
statementList ::= | statementList statement ;
statement ::= emptyStatement | expressionStatement | ifStatement | ...
            | declaration
emptyStatement ::= ';'
expressionStatement ::= expression ';'
   ...
ifStatement ::= IF '(' expression ')' statement
              | IF '(' expression ')' statement ELSE statement
declaration ::= modifiers type ID ...

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。