如何编写解析这种语言的flex和bison文件?

时间:2011-10-10 13:52:24

标签: bison yacc lex

让我们定义一种语言:

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?输入第二个后,它会提示错误并退出程序?

2 个答案:

答案 0 :(得分:1)

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

您想介绍终端:

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

并为每个打印一个单词。对于你的例子

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

这在纯粹的lex中是可行的。执行此操作时,请更新您的回复,我们可以讨论下一步

答案 1 :(得分:1)

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

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

您的yacc代码在其他方面是正确的,但是不明确,从而为您提供转换/减少冲突。那是因为你的语法含糊不清 - 像a&b|c这样的输入可以被解析为(a&b)|ca&(b|c)。您需要决定如何解决这种歧义并在语法中反映出来 - 通过使用更多的非终端,或者使用yacc的内置优先支持来解决这种歧义。如果您坚持声明:

%left '|'
%left '&'

位于yacc文件的顶部,通过使&|左关联,&优先于|来解决歧义,这将优先于$1是正常的解释。

修改

您现在遇到的问题是,您永远不会在.y文件中定义YYSTYPE(直接或使用%union),并且永远不会在.l文件中设置yylval。第一个问题意味着int等只是%s s,而不是指针(因此尝试使用%union { const char *name; } %token <name> VAR LB RB LN %left <name> AND OR %left <name> EQ %type <name> expr 打印它们没有任何意义 - 您应该从C获得警告编译器)。第二个问题意味着它们永远不会有值,所以它始终是未初始化的全局变量的默认值0

最简单的解决方法是添加

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

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

$$

最后,您还需要更改expr的bison操作以设置| LB exp RB { asprintf(&$$, "%s %s %s",$1,$2,$3); printf("abstract: %s\n", $$); } ,例如:

line

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

您遇到的最后一个问题是您的line: /* empty */ | line exp LN { printf.... 规则只匹配一行,因此第二行输入会导致错误。您需要一个递归规则,如:

{{1}}