编译原理实验要用lex,找了好久找到win_flex。
百度云链接:链接:https://pan.baidu.com/s/1b4qovNdx8_gKdtYfeoijiQ
提取码:mipa
解压后如图,里面的test,out,a都是我做实验写的,没有删也打包进去了,所以如果有和我做同样实验的就很简单了。
正则表达式的规则:
符号 | 解释,我的表述可能不太准确 |
---|---|
ab | 代表ab连接串 |
a|b | 代表a串或者b串 |
a* | 代表a串的0次或多次重复 |
a+ | 代表a串的1次或多次重复 |
a? | 代表a串的存在或不存在 |
. | 代表任意字符的匹配 |
[a-z] | 代表所有的小写字母中任意一个,[a-zA-Z]代表所有的字母中的任意一个。 |
[^a] | 代表所有不是a的字符中任意一个。 |
上面是正则表达式的写法,在lex中也是差不多的。
首先,要使用win_flex就要先编写.l文件,就是我图中的test1.l
lex文件(就是.l文件)的编写规则如下。
共可分为三个部分(我认为的)
1,在 %{ 和%}之间的为第一部分,这一部分的代码是和c的语法一样,他会被直接加在生成的test1.c文件的前面,主要是用来声明全局变量和包含头文件的。如下
%{
#include<stdio.h>
long long reserved_word_number=0;
long long special_symbol_number=0;
long long number_of_figures=0;
long long identifier_number=0;
long long space_number=0;
long long tab_number;
long long enter_number;
%}
最后一定记得是%}不是}%!
2.第二部分则是正则表达式的编写和 %% 和 %%之间的部分
reserved_word (if)|(then)|(else)|(end)|(repeat)|(until)|(read)|(write)
special_symbol ["+""\-""*""/""=""<""("")"";"]|(:=)
number [0-9]+
identifier [a-zA-Z]+
notes \{([^/}]|\n)*\}
space " "
tab "\t"
enter "\n"
%%
{reserved_word} {printf("reserved_word:%d %s\n",reserved_word_number++,yytext);fprintf(yyout,"reserved_word: %s\n",yytext);}
{special_symbol} {printf("special_symbol:%d %s\n",special_symbol_number++,yytext);fprintf(yyout,"special_symbol: %s\n",yytext);}
{number} {printf("number:%d %s\n",number_of_figures++,yytext);fprintf(yyout,"number: %s\n",yytext);}
{identifier} {printf("identifier:%d %s\n",identifier_number++,yytext);fprintf(yyout,"ID: %s\n",yytext);}
{space} {printf("space:%d %s\n",space_number,yytext);/*fprintf(yyout,"reserved_word: %s",yytext);*/}
{tab} {printf("tab:%d %s\n",tab_number,yytext);}
{enter} {printf("enter:%d %s\n",enter_number,yytext);}
{notes} {/*printf("notes:%s\n",yytext);*/fprintf(yyout,"notes:%s\n",yytext);}
%%
正则表达式的编写部分,前面是名称,中间一个空格,然后是正则表达式,里面的元字符要匹配的话要用\进行转义。对连续多个元字符进行转义可以使用一对双引号,元字符有大中小括号,+,-,* 等等。
两个%%之间的部分就是在匹配到相应的表达式时要做的事
前面一对大括号之间是表达式名称,中间一个空格,后面一对括号里是匹配到后要做的事,语法为c,只要你在第一部分写了相应的头文件,那么c的函数都可以调用,yytext为匹配到的文本,
注意:若是有一段文本没有和写的任何正则表达式匹配,则会直接输出该段文本。
3.main函数以及yywarp函数
int main(){
yyin=fopen("C:/Users/qzz/Desktop/in.txt","r");
yyout=fopen("C:/Users/qzz/Desktop/out.txt","w");
yylex();
fclose(yyin);
fclose(yyout);
getchar();
return 0;
}
int yywrap(){
return 1;
}
会被直接加在源文件里,
yytext为匹配到的相应文本,yyout为输出,yyin为输入,
yywarp()函数为读到文件末尾时要做的事,返回1代表结束
yylex()为启动词法分析的函数,默认情况下输入为键盘,输出为屏幕,可通过在main函数里写,从这里可以看出,yyout和yyin就是文件指针 。
yyin=fopen(in_filepath,"r");
yyout=fopen(out_filepath,"w");
进行更改。
写好了lex文件之后了,就是利用win_flex生成.c文件了。
好像可以直接将lex文件拖到win_flex.exe上就会生成默认文件名的lex.yy.c的源文件,然后编译后就可以使用了。
也可以在flex目录栏里直接键入cmd打开控制台然后输入,test1和Test都是我自己的文件名,使用的时候记得相应的更改,注意,第一个是输出文件名,第二个是输入文件名。第一条是生成源文件,第二条是对生成的源文件进行编译
gcc所在目录一定要加在环境变量里,当然也可以直接用编译器打开源文件然后一键编译生成。
win_flex -o test1.c test1.l
gcc -o Test.exe test1.c
用IDE进行编译的话(比如VS),可能会出现提示少了 unistd.h头文件,这个只需要新建一个头文件,名字为unistd.h然后在里面写如下代码,同时记得把#include<unistd.h>的尖括号改为引号,以及改为 X64 模式,关闭sdl检查
/** This file is part of the Mingw32 package.
* unistd.h maps (roughly) to io.h
*/
#ifndef _UNISTD_H
#define _UNISTD_H
#include <io.h>
#include <process.h>
#endif /* _UNISTD_H */
最后运行生成的exe文件,就可以进行词法分析啦,我是用的文件输入以及文件输出,结果如下
in.txt
{ Sample program
in TINY language -
computers factorial
}
read x;{input an integar}
if 0<x then { don't compute if x<=0}
fact :=1;
repeat
fact:=fact *x;
x:=x-1;
until x=0;
write fact{ output factorial of x}
end
out.txt
notes:{ Sample program
in TINY language -
computers factorial
}
reserved_word: read
ID: x
special_symbol: ;
notes:{input an integar}
reserved_word: if
number: 0
special_symbol: <
ID: x
reserved_word: then
notes:{ don't compute if x<=0}
ID: fact
special_symbol: :=
number: 1
special_symbol: ;
reserved_word: repeat
ID: fact
special_symbol: :=
ID: fact
special_symbol: *
ID: x
special_symbol: ;
ID: x
special_symbol: :=
ID: x
special_symbol: -
number: 1
special_symbol: ;
reserved_word: until
ID: x
special_symbol: =
number: 0
special_symbol: ;
reserved_word: write
ID: fact
notes:{ output factorial of x}
reserved_word: end