野牛匹配模式的顺序是什么?

问题描述

说明

我想为带有野牛的json文件实现语法检测器。

  1. 我用过
    Array: LB RB
         | LB Values RB
         | LB Values COMMA RB error
              { puts("extra comma,recovered"); };
    
    识别语法错误,例如["extra comma",],但失败。但是Object的类似模式有效。我认为这可能与模式顺序有关。
  2. 但是,当我使用
    Values: Value
          | Value COMMA Values
          | Value COMMA error
               { puts("extra comma,recovered"); }
    
    成功了。我想知道为什么
    Value COMMA error { puts("extra comma,recovered"); }
    
    不会与应该为Value COMMA Values的文本不匹配吗?

代码

Syntax.y

%{
    #include"lex.yy.c"
    void yyerror(const char*);
%}

%token LC RC LB RB COLON COMMA
%token STRING NUMBER
%token TRUE FALSE VNULL
%%

Json:
      Value
    ;
Value:
      Object
    | Array
    | STRING
    | NUMBER
    | TRUE
    | FALSE
    | VNULL
    ;
Object:
      LC RC
    | LC Members RC
    | LC Member COMMA RC error { puts("extra comma,recovered"); }
    ;
Members:
      Member
    | Member COMMA Members
    ;
Member:
      STRING COLON Value
    ;
Array:
      LB RB
    | LB Values RB
    | LB Values RC error { puts("unmatched right bracket,recovered"); }
    ;
Values:
      Value
    | Value COMMA Values
    | Value COMMA error { puts("extra comma,recovered"); }
    ;
%%

void yyerror(const char *s){
    printf("Syntax error: ");
}

int main(int argc,char **argv){
    if(argc != 2) {
        fprintf(stderr,"Usage: %s <file_path>\n",argv[0]);
        exit(-1);
    }
    else if(!(yyin = fopen(argv[1],"r"))) {
        perror(argv[1]);
        exit(-1);
    }
    yyparse();
    return 0;
}

lex.l

%{
    #include"Syntax.tab.h"
%}
%option noyywrap

unic u[0-9a-fA-F]{4}
esc \\([\"\\/bfnrt]|{unic})
scp [^"\\\x00-\x1f]
string \"({esc}|{scp})*\"

int 0|[1-9][0-9]*
frac \.[0-9]+
exp [Ee][+-]?[0-9]+
number -?{int}{frac}?{exp}?

empty [ \n\r\t]

%%

"{" { return LC; }
"}" { return RC; }
"[" { return LB; }
"]" { return RB; }
":" { return COLON; }
"," { return COMMA; }

"true" { return TRUE; }
"false" { return FALSE; }
"null" { return VNULL; }

{string} { return STRING; }
{number} { return NUMBER; }

{empty} {}

. { printf("lexical error: %s\n",yytext); }

解决方法

error令牌在无法使用前瞻性令牌执行任何操作时由野牛解析器自动生成。如果当前的解析器状态具有这种移位动作,则可以通过移位error令牌来取得进展。

除非生成了error令牌,否则不能使用包含error令牌的产品。

在移动error令牌之后,解析器将尝试匹配包含它的其余生产。如有必要,它将跳过输入,直到找到可以转移的令牌为止。可以用来同步解析。因此,看到像这样的模式很常见

Array: '[' Values ']'
     | '[' Values error ']'

试图匹配右括号。

或者,您可以按照与第二个示例类似的方式重新启动解析。 (这里我使用left-recursion,通常在列表很长的情况下是一个更好的选择,因为left-recursion不需要使用解析栈):

Values: Value
      | Values ',' Value
      | Values error

如果您想针对输入中包含尾随逗号的情况产生特定的错误,则最好的方法是仅包含一条正常规则:

Array: '(' Values ',' ')' { /* Signal the error */ }

好的错误识别是一门艺术,而不是一门科学。几乎总是需要一定数量的实验。


在大多数应用程序中,最好是立即拒绝无效的JSON,而不是尝试产生有意义的错误消息,而最好不要继续解析。 JSON通常是机器生成的,应该正确;如果不是,则没有反向通道用于发送有意义的错误,也没有人向其发送错误。格式不正确的交换字符串通常是试图引起漏洞的尝试,应这样对待。 (当然,您的申请可能是个例外。但是经验表明,立即拒绝几乎总是最好的策略,也是最简单的策略。)