YACC生成的解析器为重复的相同输入提供不同的输出

时间:2016-08-29 10:38:35

标签: c yacc lex

我想检查字符串,如果它与使用YACC和LEX的语法String sql = "UPDATE table1 SET col1=?, col2=? WHERE col1=? and col2=?"; java.sql.PreparedStatement stmt = conn.prepareStatement(sql); //conn is sql connection to DB if (time1 != null) stmt.setTimestamp(1, time1); else stmt.setNull(1, java.sql.Types.TIMESTAMP); if (time2 != null) stmt.setTimestamp(2, time2); else stmt.setNull(2, java.sql.Types.TIMESTAMP); if (time3 != null) stmt.setTimestamp(3, time3); else stmt.setNull(3, java.sql.Types.TIMESTAMP); if (time4 != null) stmt.setTimestamp(4, time4); else stmt.setNull(4, java.sql.Types.TIMESTAMP); stmt.executeUpdate(); 匹配。以下是我的LEX和YACC文件的顺序相同:

Lex档案:

a^nb^n

YACC档案:

  %{
   #include <stdio.h>
   #include <y.tab.h>
  %}

  %%
   a {return A;}
   b {return B;}
   . {return yytext[0];}
   \n {return NL;}
  %%
   int yywrap(void) {
      return 1;
   }

问题在于输出:

输入字符串:aabb

输出:已接受

输入字符串:aabb

输出:拒绝

当第二次输入相同的输入或任何假设接受的输入时,将拒绝接听。我了解到YACC在解析字符串时使用的堆栈。但我找不到任何有关这方面的文件。请帮忙。

2 个答案:

答案 0 :(得分:4)

T规则T : T S NL | /* empty */ ; 是非递归的,这意味着它只匹配一个行。以下修改将观察任意数量的行(包括无)。

$ printf 'aabb\naabb\n' | ./a.out
accepted
accepted

导致连续的行被接受。

private void NavigateFromBrowser()
 {
  using (var wb = new WebBrowser())
   {
                    wb.Width = Width;
                    wb.Height = Height;
                    wb.ScrollBarsEnabled = false;
                    wb.Navigate(_url);

                    while (wb.ReadyState != WebBrowserReadyState.Complete)
                    {
                        Application.DoEvents();
                    }

   }
}

答案 1 :(得分:2)

我无法通过重复运行您的解析器来重现您的结果,但我可以通过在同一次运行中为解析器提供多个字符串来重现它。理解差异是至关重要的:yyparse()的一次执行是一次运行,并且它将尝试解析整个输入,直到词法分析器信号结束为止。 @kdhp的答案解释了为什么你的解析器在同一次运行中不接受多个字符串,并提供了一种获得你想要的结果的方法。

另一种方法是真正执行多次运行,您可以通过调整解析器和词法分析器来执行。词法分析器在看到换行符时需要报告输入结束,然后解析器根本不需要考虑换行符。

此外,在任何一种情况下所需的词法分析器都非常简单,因此使用lex(或flex)生成它是过度的;直接编写合适的yylex()要简单得多。此外,通过自己编写,您可以更方便地在行尾和输入结束之间区分main()。一个词法分析器支持语法文件中出现的所有内容,在main()之前:

int end_of_input = 0;

int yylex(void) {
    int c = getc();  /* no buffering inside the lexer */

    if (c < 0) {
        end_of_input = 1;
    }
    return ((c == '\n') ? 0 : c);
}

为简单起见,假设解析器使用文字标记,在这种情况下没有特别的理由不这样做。此外,它所需的唯一规则是非终端S(现在是起始符号)的原始规则的此变体:

S : 'a' S 'b'
  | 'a' 'b'
  ;

使用该词法分析器和该规则,yyparse()的每次运行将解析最多一行(但可能在行尾之前停止解析错误),并且如果和,则返回0只有整行成功解析。然后在循环中运行它:

int main(void)
{
    do {
        if (yyparse() == 0)) {
            puts("accepted");
        } // else yyerror() already printed "rejected"
    } while (!end_of_input);
}

作为最后一点,请注意,如果在输入行的中间某处存在解析错误,则上述内容不会占用该行的其余部分(并且您的版本也不会消耗)。如果在出错之后,您希望继续在main()中的下一行或通过解析器中的错误规则进行解析。那当然可以做到;我把它留作练习。