一、 实验目的
构造tiny语言的词法分析器(扫描器),要求利用第三方的lex工具进行构造。构造出的扫描器,能够读入教材样例中给出的tiny语言的示例代码,分解成token输出。
掌握使用lex工具
掌握构造词法分析器
掌握LEX的输入文件的编写
二、 实验设计
掌握词法分析器的构造和使用,学会使用 fex 工具来构造词法分析程序。
1.选择教材《程序清单2-3 TINY语言中的样本程序》作为输入
2.选择教材中《程序清单2-4》作为输出
3.程序分析与设计
由TINY语言的样本程序可得,主要有注释,数字标识符,字符串标识符,关键字标识符,操作符标识符,转义相关符,其他符号
定义正则表达式如下:
分析教材的输出形式,发现要以’\n’为结尾进行输出,每次输出token的时候存好函数指针以便复现输出
三、 内容和步骤及实验结果
编译运行
flex filename.l
gcc [-o outfile] lex.yy.c
./filename.out
gdb调试
Gcc -g lex.yy,c
输入测试文本1:
{ Sample program
in TINY language -
computes factorial
}
read x; { input an integer }
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
输出结果:
输入文本2:
{ Sample program
in TINY language -
computes factorial
}
read x; { input an intger }
end
输出结果:
输入文本3:
{ Sample program
in TINY language -
computes factorial
}
输出结果:
输入文本4:
read x; { input an intger }
end
输出结果:
四、 实验代码
%{
#include<stdio.h>
#include<string.h>
#define maxn 100
int lines=1;
int tot=0;
int textlen[maxn]={0};
char* points[maxn]={NULL};
void (*funcs[maxn])(char* ,int );
int flag=0;
extern void setbegin();
extern void Printinit();
extern void PrintLine();
extern void PrintDetail();
extern void addLine();
extern void SolveKeyWord(char* tmp,int );
extern void SolveNumbers(char* tmp,int );
extern void SolveChar(char* tmp,int );
extern void SolveOperator(char* tmp,int );
extern void SolveOther(char* tmp,int );
%}
Commit "{"[^\}]*"}"
Keyword read|if|then|repeat|until|write|end
Numbers [0-9]+
Char [a-zA-Z]+
Operator "<"|":="|"<="|"*"
Delim [\t|\r|\n]
Other [" "|","|";"]
%%
{Commit} { setbegin(); Printinit(); }
{Keyword} {
setbegin();
points[++tot]=yytext;
textlen[tot]=yyleng;
funcs[tot]=SolveKeyWord;
printf("%s ",yytext);
}
{Numbers} {
setbegin();
points[++tot]=yytext;
textlen[tot]=yyleng;
funcs[tot]=SolveNumbers;
printf("%s ",yytext);
}
{Char} {
setbegin();
points[++tot]=yytext;
textlen[tot]=yyleng;
funcs[tot]=SolveChar;
printf("%s ",yytext);
}
{Operator} {
setbegin();
points[++tot]=yytext;
textlen[tot]=yyleng;
funcs[tot]=SolveOperator;
printf("%s ",yytext);
}
{Delim} {
setbegin();
//carefully
//the end of input isn't '\n' but still can output end ????
if(*yytext=='\n'){
printf("\n");
for(int i=1;i<=tot;i++){
( *( funcs[i] ) )( points[i] , textlen[i] );
}
tot=0;
flag=0;
//finish this line++
addLine(yytext);
}
}
{Other} {
setbegin();
if(*yytext==';'){
points[++tot]=yytext;
textlen[tot]=yyleng;
funcs[tot]=SolveOther;
}
}
%%
void setbegin(){
if(!flag) {
PrintLine();
flag=1;
}
}
//solve problems
void SolveKeyWord(char* tmp,int len){
PrintDetail();
printf("reserved word: ");
for(int i=0;i<len;i++) printf("%c",*(tmp+i));
printf("\n");
if( strncmp(tmp,"end",3) == 0 ){
lines++;
PrintDetail();
printf("EOF\n");
}
}
void SolveNumbers(char* tmp,int len){
PrintDetail();
printf("NUM, val= ");
for(int i=0;i<len;i++) printf("%c",*(tmp+i));
printf("\n");
}
void SolveChar(char* tmp,int len){
PrintDetail();
printf("ID, name= ");
for(int i=0;i<len;i++) printf("%c",*(tmp+i));
printf("\n");
}
void SolveOperator(char* tmp,int len){
PrintDetail();
for(int i=0;i<len;i++) printf("%c",*(tmp+i));
printf("\n");
}
void SolveOther(char* tmp,int len){
PrintDetail();
for(int i=0;i<len;i++) printf("%c",*(tmp+i));
printf("\n");
}
//solve output
void PrintLine(){
printf("%d: ",lines);
}
void PrintDetail(){
printf(" %d: ",lines);
}
void Printinit(){
for(int i=0;i<yyleng;i++){
if(*(yytext+i)=='\n'){
printf("%c",*(yytext+i) );
lines++;
PrintLine();
}
else printf("%c", *(yytext+i) );
}
}
void addLine(char* str){ if(*str=='\n') lines++; }
int yywrap(){ return 1; }
int main(int argc, char *argv[]){
yylex();
return 0;
}
五、 实验结论
1 理论基础
(1) 关于LEX工具的使用方法
(2) 掌握LEX输入文件的格式
LEX输入文件由3个部分组成:定义集、规则集、以及辅助程序集或用户程序集。这3个部分由位于新一行第1列的双百分号分开
(3)了解词法分析器的内部工作原理
(4)了解TINY语言的基本关键字和结构
TINY 的单词记号分为三种典型类型:保留字、特殊符号和“其他”单词。保留字一共 8 个,特殊符号包括运算符和界符:分别是四种基本的整数运算符号,两种比较符号(等号和小于),以及括号、分号和赋值号。除赋值号是两个字符的长度以外,其余均为一个字符 TINY 的标识符是一个或多个字母的序列。数是一个或多个数字的序列。除了单词之外,TINY 还要遵循以下词法规则:注释应放在花括号{…}中,且不可嵌套;代码应是自由格式;空白符由空格、制表位和新行组成。
2 分析和总结
- 实验中开始以为yytext是个单纯的字符指针,但是从输出的表现形式来说并不是,实际上yytext是个字符数组,yytext指向字符数组首元素的地址。
- 从输出的结果来看,我程序是以’\n’分割来进行每一行的输出,但是最后一行仍能预期完成要求,可见输入的时候将输入转换成文本的时候默认加了一个’\n’并在末尾加了一个EOF。
- 正则表达式的写对是很重要的,并且规则集是按照先后顺序匹配的,需要考虑具有相同部分的正则式优先级。
- 虽然lex能用C++来写,但是用C去模仿教材的输入能巩固函数指针以及函数指针数组的知识和一些预处理部分的知识。