
编译原理第二次实验
2024年3月29日...大约 4 分钟
编译原理第二次实验
一、实验目的
掌握词法分析器的构造原理,理解单词的类别和识别方法,掌握手工编程或 LEX 编程方法之一。
二、实验内容
编写一个词法分析器,能够将输入的源程序转换为单词序列输出。
三、实验要求
源语言定义
(1)该语言的关键字:if while do break real true false int char bool float
(其中,int、char、bool、float
在产生式中为basic
) 所有的关键字都是保留字,并且必须是小写。 (2)id和num的正则表达式定义; (3)专用符号:+ - * / < <= > >= == != = ; , ( ) [ ] { } /* */
(4)空格由空白、换行符和制表符组成。空格通常被忽略,除了它必 须分开ID、NUM关键字。 (5)程序书写格式自由:一行可以有多个语句,一个语句也可以有多 行,单词之间可以插入任意空格。 (6)考虑注释。注释由/*
和*/
包含。注释可以放在任何空白出现的位置, 且可以超过一行。注释不能嵌套。
实现词法分析器的注意要点:
(1) 关键字和标识符名的区别; (2) 数字的转换处理; (3) “>=”和“>”这类单词的处理;
四、实验过程
1. 开发方法
1.1 实验准备
- 安装Flex和MinGW-W64,并讲其安装目录的bin子目录写入环境变量
- 网上搜索LEX的书写规则,快速入门
- 阅读实验二的示例代码,并试运行查看结果
1.2 编写词法分析器
1.2.1 编写正则表达式
delim [ \t\n]
letter [A-Za-z]
digit [0-9]
comment (\/\/([^\n])+)|(\/\*([^\*])*\*([\*]|[^\*\/]([^\*])*[\*])*\/)
ws {delim}+
id ({letter}|_)({letter}|_|{digit})*
number {digit}+(\.{digit}+)?(E[+-]?{digit}+)?
string \"[^\"\n]*\"
keyword if|while|do|break|real|true|false|int|char|bool|float
bracket \{|\}|\(|\)|"["|"]"
semicolon ";"
op <|<=|!=|=|==|<>|>|>=|%|\+|-|\*|\/
- 将会用到的正则表达式写好并命名定义,方便分析与使用
- 这里将单行注释与多行注释写入同一正则表达式中
- 其他的正则表达式分别用来匹配
ID
、NUM
、字符串、关键字、操作符、分号、各种括号等
1.2.2 编写匹配处理
{ws}|{comment} {;}
{string} {printf("(string, %s)\n", yytext);}
{keyword}|{bracket}|{semicolon}|{op} {printf("%s,\n", yytext);}
{number} {printf("(num, %s)\n", yytext);}
{id} {printf("(id, %s)\n", yytext);}
"," {printf("%s\n", yytext);}
. {printf("Unknown : %s\n",yytext);}
- 由于实验要求和示例都未指明对注释应如何处理,此处对其的处理是不做处理
- 对于ID,会打印(id, %s),NUM和字符串也是类似
- 其他的如关键字等则是打印原字符串
1.2.3 编写主函数
int main(int argc, char *argv[])
{
const char *input_filename = "input";
if (argc == 2){
input_filename = argv[1];
}
yyin = fopen(input_filename, "r");
yylex();
fclose(yyin);
return 0;
}
int yywrap()
{
return 1;
}
- 此处是模仿示例一中的读取文件,方便调试
2. 测试运行结果
2.1 编写F.bat
- 为方便编译,可将编译所需命令行写入bat文件中
set filename=%1
flex %filename%.l
gcc -o out lex.yy.c
2.2 测试一
- 测试文件内容为:
{
char* str = "Hello world";
/*
这是一段注释;
*/
printf("%s",str);
}
- 运行结果如下:
2.3 测试二
- 测试文件内容为:
{
int i; int j; float v; float x; float[100] a;
while ( true) {
do i = i + 1; while ( a[i] < v);
do j = j - 1; while ( a[j] > v);
if ( i >= j ) break;
x = a[i]; a[i] = a[j]; a[j] = x;
}
}
- 运行结果如下:![[Pasted image 20240329171830.png]]![[Pasted image 20240329171910.png]]
3. 遇到的问题与解决
[! question] Q1:之前没接触过LEX,对其不熟悉 A1: 在浏览器上搜索LEX的快速入门教程,了解了LEX文件的代码结构和基本规则,以及如何配合MinGW编译运行其生成的代码,由于LEX基于正则表达式和C语言,故很快便掌握了
[! question] Q2:如何分别关键字和标识符名 A2: 将对关键字的识别处理写在标识符处理的前面,这样对关键字的识别优先级更高,避免了将关键字当成标识符
附录
- myprogram.l完整代码
%{
%}
delim [ \t\n]
letter [A-Za-z]
digit [0-9]
comment (\/\/([^\n])+)|(\/\*([^\*])*\*([\*]|[^\*\/]([^\*])*[\*])*\/)
ws {delim}+
id ({letter}|_)({letter}|_|{digit})*
number {digit}+(\.{digit}+)?(E[+-]?{digit}+)?
string \"[^\"\n]*\"
keyword if|while|do|break|real|true|false|int|char|bool|float
bracket \{|\}|\(|\)|"["|"]"
semicolon ";"
op <|<=|!=|=|==|<>|>|>=|%|\+|-|\*|\/
%%
{ws}|{comment} {;}
{string} {printf("(string, %s)\n", yytext);}
{keyword}|{bracket}|{semicolon}|{op} {printf("%s,\n", yytext);}
{number} {printf("(num, %s)\n", yytext);}
{id} {printf("(id, %s)\n", yytext);}
"," {printf("%s\n", yytext);}
. {printf("Unknown : %s\n",yytext);}
%%
int main(int argc, char *argv[])
{
const char *input_filename = "input";
if (argc == 2){
input_filename = argv[1];
}
yyin = fopen(input_filename, "r");
yylex();
fclose(yyin);
return 0;
}
int yywrap()
{
return 1;
}
Powered by Waline v3.4.1