Parsing Bison:有效表达式的GLR解析失败,没有错误消息

Parsing Bison:有效表达式的GLR解析失败,没有错误消息,parsing,bison,cobol,glr,Parsing,Bison,Cobol,Glr,我正在GNU bison中使用GLR解析器,我有以下问题: 我试图解析的语言允许布尔表达式,包括关系(、b和c),我可以在调试输出中看到,解析器的行为如下: booleanexpression : relation | booleanexpression TOK_AND booleanexpression ... ; relation : arithmeticexpression TOK_GT maxtree ... ; maxtree : ari

我正在GNU bison中使用GLR解析器,我有以下问题:

我试图解析的语言允许布尔表达式,包括关系(、b和c),我可以在调试输出中看到,解析器的行为如下:

booleanexpression
    : relation
    | booleanexpression TOK_AND booleanexpression
    ...
;
relation
    : arithmeticexpression TOK_GT maxtree
    ...
;
maxtree
    : arithmeticexpression
    | maxtree TOK_AND maxtree
    ...
;
  • 它读取
    a
    并在向前看
    时将
    a
    简化为
    算术表达式
  • 它读取
    b
    ,并在向前看
    时,将
    b
    简化为
    算术表达式
    ,然后已经简化为
    maxtree
  • 它将
    a>b
    减少为
    关系
  • 它读取
    c
    并将其简化为
    算术表达式
然后什么也没有发生。
和c
显然被丢弃了-调试输出没有显示这些令牌的任何操作。甚至没有错误消息。相应的if语句在我的AST中不存在(我仍然得到AST,因为我有错误恢复)

我会认为,在阅读了
b
之后,应该有两个堆栈。但是
b
不应该减少。或者至少它应该给我一些错误消息(“语言不明确”可以,我以前见过这个消息-我不明白为什么它不适用于这里)。有人能理解这一点吗

从一段时间的语法来看,这里的主要问题是在下一个算术表达式之后是否会出现

  • 另一个关系标记(那么您应该减少)
  • 另一个布尔合成(然后您应该移位)
  • 布尔/算术表达式语法(如THEN)之外的一个标记,它将终止表达式,并且还应该移位
你能想出一种不同的语法来更好地/更确定地描述情况吗?你会如何处理这个问题?我目前正在考虑让语法从右向左,比如

booleanexpression : relation AND booleanexpression
maxtree : arithmeticexpression AND maxtree
etc.
我想这会让野牛更喜欢移动,只会先在右边减少。也许通过使用不同的非终端,它会允许在算术表达式后面有一个准“前瞻性”

旁注:GnuCOBOL处理这个问题的方法是收集所有的标记,将它们推到中间堆栈上,然后从那里手动构建表达式。这让我很沮丧,但我还是希望他们这样做,因为bison在开始时不支持GLR解析

编辑: 一个可复制的小例子

%{
#include <stdio.h>
int yylex ();
void yyerror(const char* msg);
%}

%glr-parser
%left '&'
%left '>'

%%
input: %empty | input bool '\n' {printf("\n");};

arith : 'a' | 'b' | 'c';
maxtree : arith { printf("[maxtree : arith]  "); }
        | maxtree '&' maxtree { printf("[maxtree : maxtree & maxtree]  "); } ;
rel : arith '>' maxtree { printf("[rel : arith > maxtree]  "); } ;
bool : rel { printf("[bool : rel]  "); }
     | bool '&' bool { printf("[bool : bool & bool]  "); } ;
%%

void yyerror(const char* msg) { printf("%s\n", msg); }
int yylex () {
    int c;
    while ((c = getchar ()) == ' ' || c == '\t');
    return c == EOF ? 0 : c;
}
int main (int argc, char** argv) {
    return yyparse();
}
%{
#包括
int-yylex();
无效错误(常量字符*消息);
%}
%glr解析器
%左“&”
%左'>'
%%
输入:%empty | input bool'\n'{printf(“\n”);};
arith:‘a’|‘b’|‘c’;
maxtree:arith{printf(“[maxtree:arith]”;}
|maxtree'&'maxtree{printf(“[maxtree:maxtree&maxtree]”;};
rel:arith'>'maxtree{printf(“[rel:arith>maxtree]”;};
bool:rel{printf(“[bool:rel]”;}
|bool'&bool{printf(“[bool:bool&bool]”);
%%
void yyerror(const char*msg){printf(“%s\n”,msg);}
int-yylex(){
INTC;
而((c=getchar())=''|| c=='\t');
返回c==EOF?0:c;
}
int main(int argc,字符**argv){
返回yyparse();
}

奇怪的是,这一条确实在输入
a>b&c

上打印了错误消息“syntax error”(语法错误)。通过使用优先级声明简化语法确实很方便(有时)[注1],但它不能很好地使用GLR解析器,因为它可能导致早期拒绝明确的解析

优先级声明背后的思想是解决歧义(或者更准确地说,转移/减少冲突)使用一个简单的单令牌前瞻,并在可能的缩减和可能的移位之间配置优先级。如果语法没有移位/缩减冲突,则不会使用优先级声明,但如果使用它们,则将根据(静态)优先级关系,使用它们来抑制移位或缩减

Bison生成的GLR解析器实际上并不解决歧义,但它允许继续开发可能不正确的解析,直到语法解决歧义。与使用优先级不同,这是一种延迟解析;速度稍慢,但功能更强。(GLR解析器可以生成“解析林”包含所有可能的解析。但Bison没有实现此功能,因为它期望解析编程语言,并且与人类语言不同,编程语言不能含糊不清。)

在你的语言中,不可能静态地解决移位/减少冲突的不确定性,正如你在问题中所指出的那样。你的语法根本不是LR(1),更少的运算符优先级,因此GLR解析是一个实用的解决方案。但您必须允许GLR完成其工作。过早地使用优先级比较来消除其中一个看似合理的解析将阻止GLR算法在以后考虑它。如果您设法消除唯一与uld是正确的

在语法中,不可能定义
rel
产品和
&
符号之间的优先关系,因为不存在优先关系。在某些句子中,
rel
缩减需要赢;在其他句子中,移位应该赢。因为语法不含糊,GLR最终会赢找出哪个是哪个,只要换档和减速都允许进行

在您的完整语言中,布尔表达式和算术表达式都有类似于运算符优先级的内容,但仅在其各自的域中。运算符优先级解析器(以及等效的yacc/bison优先级声明)其工作原理是消除不同非终端之间的差异;它无法处理像您这样的语法,其中某些操作
                              %left '+' '-'
                              %left '*' '/'
%%                            %%
expr  : term
      | expr '+' term         expr: expr '+' expr
      | expr '-' term             | expr '-' expr
term  : factor
      | term '*' factor           | expr '*' expr
      | term '/' factor           | expr '/' expr
factor: ID                        | ID
      | '(' expr ')'              | '(' expr ')'
%code top {
#define _GNU_SOURCE 1
}

%{
#include <ctype.h>
#include <stdio.h>
#include <string.h>

int yylex(void);
void yyerror(const char* msg);
%}

%define api.value.type { char* }
%glr-parser
%token ID

%%
input   : %empty
        | input bool '\n'   { puts($2); }

arith   : ID
maxtree : arith 
        | maxtree '&' arith { asprintf(&$$, "[maxtree& %s %s]", $1, $3); }
rel     : arith '>' maxtree { asprintf(&$$, "[COMP %s %s]", $1, $3); }
bool    : rel
        | bool '&' rel      { asprintf(&$$, "[AND %s %s]", $1, $3); }
%%

void yyerror(const char* msg) { printf("%s\n", msg); }
int yylex(void) {
    int c;
    while ((c = getchar ()) == ' ' || c == '\t');
    if (isalpha(c)) {
      *(yylval = strdup(" ")) = c;
      return ID;
    }
    else return c == EOF ? 0 : c;
}

int main (int argc, char** argv) {
#if YYDEBUG
    if (argc > 1 && strncmp(argv[1], "-d", 2) == 0) yydebug = 1;
#endif
    return yyparse();
}
$ bison -t -o glr_prec.c glr_prec.y
glr_prec.y: warning: 1 shift/reduce conflict [-Wconflicts-sr]
$ gcc -Wall -o glr_prec glr_prec.c
$ ./glr_prec
a>b
[COMP a b]
a>b & c
[COMP a [maxtree& b c]]
a>b & c>d
[AND [COMP a b] [COMP c d]]
a>b & c & c>d
[AND [COMP a [maxtree& b c]] [COMP c d]]
a>b & c>d & e
[AND [COMP a b] [COMP c [maxtree& d e]]]
$
   IDENTIFICATION DIVISION.
   PROGRAM-ID EXAMPLE.
   DATA DIVISION.
   WORKING-STORAGE SECTION.
   01     A     PIC 9 VALUE 2.
   01     B     PIC 9 VALUE 1.
   01     W     PIC 9 VALUE 3.
       88 C           VALUE 3.
   PROCEDURE DIVISION.
       IF A > B AND C
          DISPLAY 'A > B AND 88 LEVEL C is TRUE because W = ' W
       ELSE
          DISPLAY 'A not > B or 88 LEVEL C is not TRUE'
       END-IF
       DISPLAY 'A: ' A ' B: ' B ' W:' W  
       GOBACK
       .
A > B AND 88 LEVEL C is TRUE because W = 3
A: 2 B: 1 W: 3
%skeleton "glr.cc"