让我们定义一种语言:
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?输入第二个后,它会提示错误并退出程序?
答案 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)|c
或a&(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}}