问题描述
说明
我想为带有野牛的json文件实现语法检测器。
- 我用过
识别语法错误,例如Array: LB RB | LB Values RB | LB Values COMMA RB error { puts("extra comma,recovered"); };
["extra comma",]
,但失败。但是Object
的类似模式有效。我认为这可能与模式顺序有关。 - 但是,当我使用
成功了。我想知道为什么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通常是机器生成的,应该正确;如果不是,则没有反向通道用于发送有意义的错误,也没有人向其发送错误。格式不正确的交换字符串通常是试图引起漏洞的尝试,应这样对待。 (当然,您的申请可能是个例外。但是经验表明,立即拒绝几乎总是最好的策略,也是最简单的策略。)