几天前,我开始使用yacc / flex进行编程,但我不知道为什么我不能只在第31行做递归调用(s:SKIP {System.out.println(“ txt:” + $ 1 );} s)。递归调用使我无需使用括号即可保持数字自由文本。我在做什么错了?
%{
import java.io.*;
%}
%token OPEN_PAREN;
%token CLOSE_PAREN;
%token <sval> SKIP;
%start s
%%
parens : OPEN_PAREN s CLOSE_PAREN
| OPEN_PAREN CLOSE_PAREN
exp : parens
exps : exp SKIP { System.out.println("S: "+$2); }
| exp
s : SKIP { System.out.println("txt: "+$1); } s
| exps
| s exps
%%
void yyerror(String s)
{
System.out.println("err:"+s);
System.out.println(" :"+yylval.sval);
}
static Yylex lexer;
int yylex()
{
try {
return lexer.yylex();
}
catch (IOException e) {
System.err.println("IO error :"+e);
return -1;
}
}
public static void main(String args[])
{
System.out.println("[Quit with CTRL-D]");
Parser par = new Parser();
lexer = new Yylex(new InputStreamReader(System.in), par);
par.yyparse();
}
答案 0 :(得分:0)
该摘录中只有一个移位/减少冲突,这是s
中模棱两可的递归性的结果。由于它与中间规则操作无关,因此可以简化为以下内容:
s: BEFORE s
| AFTER
| s AFTER
这是BEFORE* AFTER+
的语法。 (语法中的AFTER
实际上是非终结符的事实也不会改变任何东西。)
这是模棱两可的,因为应用第一个和最后一个产生式的顺序不受语法的限制。即使在简单的输入中
BEFORE AFTER AFTER
有两个不同的解析:
s s
/ \ / \
/ \ / \
BEFORE s s AFTER
| / \ / \ |
| AFTER s BEFORE s |
| | | | | |
BEFORE AFTER AFTER BEFORE AFTER AFTER
对您而言,应用哪个解析可能都无关紧要(尽管实际上适用,因为它会影响操作的执行顺序)。但这对bison / yacc很重要,因为后者不知道命令的重要性。
要解决冲突,我们需要指定一个顺序。有两种可能性:
前缀前的后缀。这是编程语言中通常使用的顺序,其中后缀运算符(例如数组下标)比前缀运算符(例如一元减号)绑定更紧密,因此-a[1]
与-(a[1])
相同,而不是{{ 1}}。在这种情况下,语法将如下所示:
(-a)[1]
后缀之前的前缀。这样做的好处是,前缀部分中的操作先于后缀部分中的操作执行。 (但是请注意,s: t
| BEFORE s
t: AFTER
| t AFTER
的右递归性意味着,如果其生产中有操作,则这些操作将从右到左执行。)
t
此替代方案的完全左递归实现:
s: t
| s AFTER
t: AFTER
| BEFORE t