使用Bison实现语法。语法控制流意外

时间:2015-08-25 11:57:05

标签: c parsing grammar bison flex-lexer

抽象语法。实际语法如下:

start ->  t1  V1;

V1 -->   t2   V1
       | t3   V2
       ;

V2 -->   t4
       | /* Empty */
       ;

当控件在V2中并且遇到令牌t3时。控件返回V1。可以这一点。

但是控件并没有在V1停止触发t3,但是重新开始,这就是问题。

实际语法。说明如下。

%{
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>

void yyerror (char *s); 
extern FILE *yyin;

%}

%token MAIN   

%token INT_DT INT_LIT   /* t_INT_DT --> Integer Data type, i.e. "int"
                         * t_INT_LIT --> An integer, i.e. "5".
                         */

%token VAR              // A variable

%token SCANF  PRINTF    // For the printf and the scanf statements.

%token IF     ELSE      // For the if and else statements.
%token GOTO

%token OPRTR            // For the operator in an expression.

/* Begining of a basic block, BBS, Basic Block Statement.
 * BBS_ST = "<bb "
 * BBS_END1 ">:"
 * BBS_END2   ">;"
 * Example:   
 * <bb 2>:
 *      Statements;
 *      goto <bb 3>;
 */
%token BBS_ST   // "<bb "// it is "<bb ".
%token BBS_END1 // ">:" 
%token BBS_END2 // ">;" 

// Opening and closing Braces, i.e. { & }
%token OP_BR  // "{" 
%token CL_BR  // "}"

// For opening and closing Parantheses, ( & )
%token OP_PR  //"("  
%token CL_PR  //")"

%token EQUAL  // "="
%token COMMA  // ","       
%token NBSP   // "&"
%token SM_CLN // ";"       // For ";" 

%token RETURN           // For the return statement.

%start begin

%%

begin:   MAIN    main   ;

main:    OP_BR    functn   CL_BR   ;

functn:   INT_DT   VAR      SM_CLN   functn  /*Recursion to stay in functn */
        | BBS_ST   INT_LIT  BBS_END1  bb     
        ;

bb:    SCANF    var_List   CL_PR    SM_CLN  bb
    |  PRINTF   var_List   CL_PR    SM_CLN  bb
    |  VAR      EQUAL      expr     SM_CLN  bb
    |  IF       OP_PR      expr     CL_PR   bb
    |  ELSE     bb
    |  GOTO     BBS_ST     INT_LIT  BBS_END2
    |  RETURN   SM_CLN    
    |  /* Empty Rule. */  
    ;  /* bb has can't catch BBS_ST as first token, thus it must return
        * to the calling rule and functn can catch BBS_ST. */

var_List:    COMMA   VAR   var_List 
          |  /* Empty Rule. */ 
          ;

expr:     VAR      expr2 
      |   INT_LIT  expr2  
      ;

expr2:    OPRTR    expr3  
       |  /* Empty Rule */
       ;

expr3:    VAR     
       |  INT_LIT  
       ;

%%
int main (int argc, char *argv[])
{
    #if YYDEBUG == 1
        extern int yydebug;
        yydebug = 1;
    #endif

    if (argc == 2)
    {
        yyin = fopen (argv [1], "r");
        if (yyin == NULL)
            perror (argv [1]); 
    }

    yyparse ();

    fclose (yyin);
    return 0;    
}


void yyerror (char *s)
{
    printf ("Error: %s\n", s);
}

到目前为止在答案中提出的想法的实施,但这并没有帮助。

begin:  MAIN main ; 

main:   OP_BR   functn_Decls   CL_BR 
        ; 

functn_Decls:   functn_Decls INT_DT VAR SM_CLN 
              | functn 
              ; 

functn:    functn BBS_ST INT_LIT BBS_END1 
        |  bb 
        ; 

bb:    bb   stmnt 
    |  /* Empty Rule */ 
    ; 

stmnt:   t1  // terminals only. 
       ; 

var_List:   t2 // terminal just for illustration. 
            ;

expr:  t3 // terminal just for illustration.
       ;

导致输入错误:

main ()
{
  <bb 2>:
  goto <bb 4>;

  <bb 3>:
}

错误:

<bb处,会触发令牌BBS_ST。 此时控件在bb中,如bb - &gt; 等待令牌 由于bb中没有规则以BBS_ST开头,因此它返回到调用规则。

现在调用规则是functn,它有一个以BBS_ST开头的规则。 问题是,此规则不会被调用。

相反,控件在 P1 处到达functn的父级。 如果在P1我添加 BBS_ST ,它会捕获此令牌。

请指出为什么会这样发生?

对应的Flex文件:

%{
    // Include the file
    #include "Parse_One_Line2.tab.h"
    extern YYSTYPE  yylval;
    extern char    *yytext;
%}

LB   ^[ \t]*
CH   [a-zA-Z]
DI   [0-9]

%%

{LB}main[ ]*\([.]*\)    return MAIN; // the main() call.

{LB}int[ ]              return INT_DT;

[+-]?{CH}({CH}{DI}_)*   return VAR; 

[+-]?[0-9]+             return INT_LIT;  

{LB}scanf[ ]?\(\".*\"   return SCANF;

{LB}printf[ ]?\(\".*\"  return PRINTF;

{LB}if[ ]               return IF; // If statement.

{LB}else[ ]             return ELSE; // If statement.

{LB}goto[ ]             return GOTO;

{LB}return              return RETURN;

\{CLOBBER\};            ;// Ignore it.

"<bb "                  return BBS_ST;  // next integer is a BB Index.

">:"                    return BBS_END1; // token Greater than Colon.

">;"                    return BBS_END2;

^;;[^\n]*[\n]           ; // Ignore it, comment.

"+"|"-"|"*"|"/"|"%"|"=="|">="|"<="|"<"|">"|"!=" return OPRTR; 

^[{]                    return OP_BR; // It is "{".

^[}]                    return CL_BR; // It is "}".

"("                     return OP_PR; // Paranthesis Open

")"                     return CL_PR; // Paranthesis Close


";"                     return SM_CLN;

"="                     return EQUAL;

"&"                     return NBSP;

","                     return COMMA;

[\n]                    ; /* Skip these characters. */

.                       ; /* Skip.   */

%%


int yywrap (void)
{
    return 1;
}

使用YYDEBUG标志的输出文件。

Starting parse
Entering state 0
Reading a token: Next token is token MAIN ()
Shifting token MAIN ()
Entering state 1
Reading a token: Next token is token OP_BR ()
Shifting token OP_BR ()
Entering state 3
Reading a token: Next token is token INT_DT ()
Shifting token INT_DT ()
Entering state 6
Reading a token: Next token is token VAR ()
Shifting token VAR ()
Entering state 9
Reading a token: Next token is token SM_CLN ()
Shifting token SM_CLN ()
Entering state 12
Reading a token: Next token is token BBS_ST ()
Shifting token BBS_ST ()
Entering state 7
Reading a token: Next token is token INT_LIT ()
Shifting token INT_LIT ()
Entering state 10
Reading a token: Next token is token BBS_END1 ()
Shifting token BBS_END1 ()
Entering state 13
Reading a token: Next token is token GOTO ()
Shifting token GOTO ()
Entering state 20
Reading a token: Next token is token BBS_ST ()
Shifting token BBS_ST ()
Entering state 29
Reading a token: Next token is token INT_LIT ()
Shifting token INT_LIT ()
Entering state 38
Reading a token: Next token is token BBS_END2 ()
Shifting token BBS_END2 ()
Entering state 47
Reducing stack by rule 10 (line 76):
   $1 = token GOTO ()
   $2 = token BBS_ST ()
   $3 = token INT_LIT ()
   $4 = token BBS_END2 ()
-> $$ = nterm bb ()
Stack now 0 1 3 6 9 12 7 10 13
Entering state 22
Reducing stack by rule 4 (line 68):
   $1 = token BBS_ST ()
   $2 = token INT_LIT ()
   $3 = token BBS_END1 ()
   $4 = nterm bb ()
-> $$ = nterm functn ()
Stack now 0 1 3 6 9 12
Entering state 14
Reducing stack by rule 3 (line 67):
   $1 = token INT_DT ()
   $2 = token VAR ()
   $3 = token SM_CLN ()
   $4 = nterm functn ()
-> $$ = nterm functn ()
Stack now 0 1 3
Entering state 8
Reading a token: Next token is token BBS_ST ()
Error: popping nterm functn ()
Stack now 0 1 3
Error: popping token OP_BR ()
Stack now 0 1
Error: popping token MAIN ()
Stack now 0
Cleanup: discarding lookahead token BBS_ST ()
Stack now 0

2 个答案:

答案 0 :(得分:1)

我重新组织了一些规则。问题是当functn看到bb时,它只需要一个GOTO。因此,当您达到functn时,预计不会有进一步的令牌。允许在bb语句后显示functn: INT_DT VAR ";" functn | bb_stmt functn | bb_stmt ; bb_stmt: BBS_ST /*P2*/ INT_LIT ">:" bb ; 应该解决此问题,并为您提供您正在寻找的行为。

main:    "{"     functn_decls /*P1*/   "}"   ;

functn_decls: INT_DT VAR ";" functn_decls
            | functn
            ;

functn: bb_stmt functn
      | bb_stmt
      ;

如果你想继续强制语句只在函数的顶部,你可以做以下的事情:

$sql_s="SELECT column FROM products LIMIT 10 WHERE id_product={$id}";

答案 1 :(得分:1)

你的语法不符合你的想法。 (这里,我只是使用简化语法,因为它比涉及整个语法更简单,并且如所示,原理是相同的。)

start →  t1  V1

V1    →  t2   V1
      |  t3   V2

V2    →  t4
      |  /* Empty */

让我们问一个简单的问题:可以跟随V2的内容?因为在语法中V2使用V1 → t3 V2的唯一地方是生产V1,答案只能是:V1之后完全相同的令牌。

那么V1可以关注什么?同样,只有两个地方使用start → t1 V1,两者都在制作结束时。一个是递归的,因此它对跟随集没有贡献,另一个在V1。因此,我们可以得出结论,唯一可以跟随V2的标记(因此唯一可以跟随$的标记)是输入结束标记,通常写为t3

所以V1无法关注V2t1 t3 t3 ,结果就是句子:

t3

不是语言的一部分;第二个statement1 ; statement2 ; statement3 ; 无法解析。

更一般地说,您似乎试图分析生成的解析器的行为,就好像它是一个自上而下的递归下降解析器。 Bison不会产生自上而下的解析器;它生成自下而上的LALR(1)解析器(默认情况下),你的“控制流”概念与LR(k)算法中发生的任何事情都不匹配。

此外,LR(1)语法对左递归没有任何问题,因此不必像对自上而下的解析器生成器那样破坏语法。您会发现左递归效果更好,因为它实际上反映了语言的结构。如果你分析

Program → ε | Statement ; Program

使用右递归语法:

statement

你会发现 statement1 ; statement2 ; statement3 ; → statement1 ; statement2 ; statement3 ; Program (Program → ε) → statement1 ; statement2 ; Program (Program → Statement ; Program) → statement1 ; Program (Program → Statement ; Program) → Program (Program → Statement ; Program) 缩减从右到左应用,这可能是从你的角度来看的。那是因为LR解析器产生最右边的推导,所以在它到达输入的末尾之前它不会开始减少:

S  → t1 | S V1 ';'
V1 → t2 | t3 V2
V2 → t4 | /* Empty */

最有可能的是你真正想要的更像是以下内容(回到你的语法减少之类):

S

使t1成为t2后跟t3t3 t4url_for的任意数字(可能为0),后面跟着一个分号