【问题标题】:How to write flex and bison file parsing this language?如何编写解析这种语言的 flex 和 bison 文件?
【发布时间】:2011-10-10 13:52:24
【问题描述】:

让我们定义一种语言:

VAR := [0-9A-Za-z_]+
Exp := VAR 
   | VAR,'=',VAR 
   | '(', Exp, ')'
   | Exp, '&', Exp 
   | Exp ,'|', Exp       

例如:“( a = b ) & ( c | (d=e) ) ”是合法的

我已经阅读了 YASS 和 Lex 手册,但我很困惑,我只想要可以解析这种语言的编译器
你能告诉我如何为这种语言编写 flex&bison 配置文件吗?

到目前为止我已经完成了:

文件 a.l:

%{

#include <string.h>
#include "stdlib.h"
#include "stdio.h"
#include "y.tab.h"

%}

%%

("&"|"and"|"AND")   { return AND; }
("|"|"or"|"OR")   { return OR; }
("="|"eq"|"EQ")   { return EQ; }
([A-Za-z0-9_]+)   { return VAR;}
("(")   { return LB ;}
(")")   { return RB ;}
("\n")   { return LN ;}



%%

int main(void)
{
 yyparse();
 return 0;
}

int yywrap(void)
{
 return 0;
}

int yyerror(void)
{
  printf("Error\n");
  exit(1);
}

文件 a.y

%{
#include <stdio.h>
%}

%token AND OR EQ VAR LB RB LN

%left AND OR
%left EQ

%%

line : 
       | exp LN{ printf("LN: %s",$1);}
;

exp:    VAR             { printf("var:%s",$1);}
    |  VAR EQ VAR      { printf("var=:%s %s %s",$1,$2,$3);}
    |  exp AND exp      { printf("and :%s %s %s",$1,$2,$3);}
    |  exp OR exp      { printf("or :%s %s %s",$1,$2,$3);}
    |  LB exp RB      { printf("abstract :%s %s %s",$1,$2,$3);}    

    ;

现在我在 Chris Dodd 的指导下编辑了文件,看起来好多了(至少 lex 工作得很好),但是我得到了这样的输出:

disk_path>myprogram
a=b
var=:(null) (null) (null)LN: (null)ab=b
Error

那么,为什么函数 printf 输出 null 呢?输入第二个后提示Error并退出程序?

【问题讨论】:

  • 向我们展示您的尝试。我会在回复中给出一些提示,但您确实应该首先展示您尝试过的内容

标签: bison yacc lex


【解决方案1】:

首先编写一个 lex 文件来标记输入(并打印出它看到的内容)

您要介绍终端:

  • [0-9A-Za-z_]+ --&gt; VAR
  • ( --&gt; LPAREN) --&gt; RPAREN
  • &amp; --&gt; AND
  • | --&gt; OR
  • = --&gt; EQUAL

然后为每个打印一个单词。以你为例

( a = b ) & ( c | (d=e) ) --> LPAREN VAR EQUAL VAR RPAREN AND LPAREN VAR OR LPAREN VAR EQUAL VAR RPAREN RPAREN

这在纯 lex 中是可行的。当您这样做时,请更新您的回复,我们可以讨论下一步

【讨论】:

    【解决方案2】:

    您的 lex 规则 ("[0-9A-Za-z_]+") 将(仅)匹配文字字符串 [0-9A-Za-z_]+ -- 去掉 " 字符,使其成为匹配任何标识符或数字的模式。

    您的 yacc 代码与标点符号的 lex 代码不匹配 - lex 代码为 &amp; 返回 AND,而 yacc 代码需要 &amp; - 所以要么更改 lex 代码以返回 '&amp;'或更改 yacc 代码以使用令牌 AND,同样适用于 |()。您可能还想忽略 lex 代码中的空格(而不是将它们视为错误)。即使您在 yacc 语法中使用了 '\n',您也没有 lex 规则来匹配和返回。

    您的 yacc 代码在其他方面是正确的,但不明确,因此会给您带来 shift/reduce 冲突。那是因为你的语法是模棱两可的——像a&amp;b|c 这样的输入可以被解析为(a&amp;b)|ca&amp;(b|c)。您需要决定如何解决这种歧义并将其反映在您的语法中——或者通过使用更多的非终结符,或者通过使用 yacc 的内置优先级支持来解决这种歧义。如果你坚持声明:

    %left '|'
    %left '&'
    

    在您的 yacc 文件的顶部,这将通过使 &amp;| 保持关联,并且 &amp; 的优先级高于 | 来解决歧义,这将是正常的解释。

    编辑

    您现在遇到的问题是您从未在 .y 文件中定义 YYSTYPE(直接或使用 %union),并且您从未在 .l 文件中设置 yylval。第一个问题意味着$1 等只是ints,而不是指针(因此尝试用%s 打印它们是没有意义的——你应该从你的C 编译器那里得到一个警告)。第二个问题意味着它们无论如何都没有值,所以它始终是未初始化全局变量的默认值 0

    最简单的解决方法是添加

    %union {
        const char *name;
    }
    %token <name> VAR LB RB LN
    %left <name> AND OR
    %left <name> EQ
    %type <name> expr
    

    到 yacc 文件的顶部。然后将所有 lex 规则更改为类似

    ([A-Za-z0-9_]+)   { yylval.name = strdup(yytext); return VAR;}
    

    最后,你还需要将 expr 的 bison 动作更改为设置$$,例如:

    |  LB exp RB      { asprintf(&$$, "%s %s %s",$1,$2,$3);  printf("abstract: %s\n", $$); }
    

    这至少会起作用,尽管它会为分配的字符串泄漏大量内存。

    您遇到的最后一个问题是您的line 规则仅匹配一行,因此第二行输入会导致错误。您需要一个递归规则,例如:

    line: /* empty */
        | line exp LN { printf....
    

    【讨论】:

    • 谢谢你,我按照你说的编辑了.l和.y文件,但是新的问题来了,看上面的描述
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-23
    • 2018-11-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多