一、实验目的
设计实现一个词法分析程序,加深对词法分析原理的理解
二、实验仪器
Visual studio
三、实验内容及原理
(1)设一小型编译程序关于高级语言有如下的规定:
高级语言程序具有四种基本结构:顺序结构﹑选择结构﹑循环结构和过程。为了便于掌握编译的核心内容,突出重点,简化编译程序的结构,同时又涵盖高级语言程序的基本结构,我们选取赋值语句﹑if语句和while语句作为前三种结构的代表,略去了过程结构。实际上,上述三种语句已经基本满足了高级语言的程序设计。因此,我们仅考虑由下面产生式所定义的程序语句:
S→if B then S else S ︱while B do S ︱begin L end︱A
L→S;L︱S
A→i:=E
B→B∧B︱B∨B︱¬ B︱(B) ︱i rop i︱i
E→E+E︱E*E︱(E) ︱i
其中,各非终结符的含义如下:
S——语句;
L——语句串;
A——赋值句;
B——布尔表达式;
E——算术表达式。
各终结符的含义如下:
i ——整型变量或常数,布尔变量或常数;
rop ——六种关系运算符的代表;
; ——起语句分隔符作用;
:= ——赋值符号;
¬ ——逻辑非运算符“not”;
∧ ——逻辑与运算符“and”;
∨ ——逻辑或运算符“or”;
+ ——算术加运算符;
* ——算术乘运算符;
( ——左括号;
) ——右括号。
注意,六种关系运算符分别为
<:小于 <=:小于等于 <>:不等于
>:大于 >=:大于等于 =:等于
关于表达式的运算,我们规定由高到低优先顺序为算术运算、关系运算、布尔运算;并且服丛左结合规则。算术运算符优先级的顺序依次为“( )”﹑“*” ﹑“+” ;布尔运算符优先级的顺序依次为“¬ ”﹑“∧”﹑“∨”;六个关系运算符优先级相同。
我们规定的程序是由一条语句或由begin和end嵌套起来的复合语句组成的,并且规定在语句末要加上“#~”表示程序结束。下面给出的是符合规定的程序示例:
begin
A:=A+B*C;
C:=A+2;
while A<C and B<D do
while A>B do
if M=N then C:=D
else while A<=D do
A:=D
end#~
(2)该小型编译程序关于单词的内部定义
由于我们规定的程序语句中涉及单词较少,故在词法分析阶段忽略了单词输入错误的检查,而将编译程序的重点放在中间代码生成阶段。词法分析器的功能是输入源程序,输出单词符号。我们规定输出的单词符号格式为如下的二元式:(单词种别,单词自身的值)
我们对常量,变量,临时变量,保留关键字(if、while、begin、end、else、then、do等),关系运算符,逻辑运算符,分号,括号等,规定其内部定义如表1所示。
表1 关于单词的内部定义
实验要求:
请对下列程序进行词法分析,输出单词的种别编码和值:
begin
begin
begin
a:=1
end;
begin
b:=1
end;
q:=10
end;
x:=9;
y:=11;
z:=12
end
#
四、实验过程原始数据记录
主要结构:
typedef struct //词的结构(单词种别,单词自身的值)
{
int typenum; //单词种别
const char* word;
}WORD;
主要函数:
char get_ch() 从输入缓冲区读取一个字符到ch中
void removal_White( ) 去掉空白字符
void concat( ) 拼接单词
int letter( ) 判断输入字符是否是字母
int digit( ) 判断输入字符是否是数字
int reserve( ) 检索关键字表格,判断单词是否为关键字
void retract( ) 回退一个字符
WORD * scanner( ) 词法扫描程序,返回值是二元组
结果:
五、实验总结
在进行编译的时候,有出现编译错误:
编译出错信息:错误 1 error C4996: 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
后来我上网查询了一下,意思就是fopen不安全,推荐你用fopen_s。
解决上面问题后,我成功运行,但在运行时又实验结果出现了偏差
我后来在不同地方的错误提示增加区别,找到了错误提示的地方在词法扫描时的Switch语句。
这意味着我在消除空白即消除换行空字符时没有处理好,我在编辑data.txt发现是tab空格没有消除
于是在去除空白的函数removal_White()加上ch==9的情况。最后成功运行并输出单词的种别编码和值
实验代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#define _CRT_SECURE_NO_WARNINGS
#define _KEY_WORDEND "waiting for your expanding"
using namespace std;
typedef struct //词的结构(单词种别,单词自身的值)
{
int typenum; //单词种别
const char* word;
}WORD;
char input[255];
char token[255] = "";
int p_input; //指针
int p_token;
char ch;
const char* rwtab[] = { "if","then","else","while","begin","do","end","and","or","not","S","L",_KEY_WORDEND};
WORD* scanner();//扫描
int main()
{
int over = 1;
WORD* oneword = new WORD;
//实现从文件读取代码段
FILE* fp;
if ((fp = freopen("data.txt", "r", stdin)) == NULL)
{
printf("Not found file!\n");
return 0;
}
else
{
while ((scanf("%[^*]", &input)) != EOF)
{
p_input = 0;
printf("your words:\n%s\n", input);
while (over < 1000 && over != -1)
{
oneword = scanner();
if (oneword->typenum < 1000)
{
if (oneword->typenum != 999)
cout << "[ " << oneword->typenum << "\t" << oneword->word << " ]" << endl;
}
over = oneword->typenum;
}
scanf("%[^*]", input);
}
}
return 0;
}
//从输入缓冲区读取一个字符到ch中
char get_ch()
{
ch = input[p_input];
p_input++;
return ch;
}
//去掉空白
void removal_White()
{
while (ch == ' ' || ch == 10 || ch == 9)
{
ch = input[p_input];
p_input++;
}
}
//拼接单词
void concat()
{
token[p_token] = ch;
p_token++;
token[p_token] = '\0';
}
//判断是否字母
int letter()
{
if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z')
return 1;
else
return 0;
}
//判断是否数字
int digit()
{
if (ch >= '0' && ch <= '9')
return 1;
else
return 0;
}
//检索关键字表格
int reserve()
{
int i = 0;
while (strcmp(rwtab[i], _KEY_WORDEND))
{
if (!strcmp(rwtab[i], token))
return i + 1;
i++;
}
return 56;//如果不是关键字,则返回种别码10
}
//回退一个字符
void retract()
{
p_input--;
}
//词法扫描程序
WORD* scanner()
{
WORD* myword = new WORD;
myword->typenum = 10; //初始值
myword->word = "";
p_token = 0; //单词缓冲区指针
get_ch();
removal_White();//去掉空白
if (letter())//判断读取到的首字母是字母
{
//如int
while (letter() || digit())
{
concat(); //连接
get_ch();
}
retract(); //回退一个字符
myword->typenum = reserve();//判断是否为关键字,返回种别码
if (myword->typenum == 56) myword->word = "ident";
else myword->word = token;
return myword;
}
else if (digit()) //判断读取到的单词首字符是数字
{
while (digit()) //所有数字连接起来
{
concat();
get_ch();
}
retract();
//数字单词种别码统一为57
myword->typenum = 57;
myword->word = "inconst";
return(myword);
}
else
{
switch (ch)
{
case '=':
get_ch();
if (ch == '=')
{
myword->typenum = 18;
myword->word = "rop";
return(myword);
break;
}
retract();
myword->typenum = 17;
myword->word = "becomes";
return(myword);
break;
case '+':
myword->typenum = 15;
myword->word = "plus";
return(myword);
break;
case '*':
myword->typenum = 16;
myword->word = "times";
return(myword);
break;
case '(':
myword->typenum = 24;
myword->word = "(";
return(myword);
break;
case ')':
myword->typenum = 25;
myword->word = ")";
return(myword);
break;
case ':':
get_ch();
if (ch == '=')
{
myword->typenum = 12;
myword->word = "a";
return(myword);
break;
}
else
break;
case ';':
myword->typenum = 13;
myword->word = ";";
return(myword);
break;
case '>':
get_ch();
if (ch == '=')
{
myword->typenum = 18;
myword->word = "rop";
return(myword);
break;
}
retract();
myword->typenum = 18;
myword->word = "rop";
return(myword);
break;
case '<':
get_ch();
if (ch == '=')
{
myword->typenum = 18;
myword->word = "rop";
return(myword);
break;
}
else
{
retract();
myword->typenum = 18;
myword->word = "rop";
return (myword);
}
case '!':
get_ch();
if (ch == '=')
{
myword->typenum = 18;
myword->word = "rop";
return(myword);
break;
}
retract();
myword->typenum = -1;
myword->word = "ERROR";
return(myword);
break;
case '#':
myword->typenum = 14;
myword->word = "jinghao";
return (myword);
break;
default:
myword->typenum = -1;
myword->word = "ERROR";
return(myword);
break;
}
}
}