编译原理-词法分析器
词法分析程序
一、实验目的
通过设计编制调试一个具体的词法分析程序,加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。
编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类。并依次输出各个单词的类型码及单词符号的自身值。(遇到错误时可显示“Error”,然后跳过错误部分继续显示)
二、实验要求
用C或C++写一个简单的词法分析程序,程序可以满足下列要求:
1、能分析如下几种简单的语言词法
(1) 标识符: ID=letter(letter|digit)*
(2) 关键字(全部小写)
main int float double char if then else switch case break continue while do for
(3)整型常量:NUM=digit digit*
(4)运算符: = + - * / < <= == != > >= ; ()? :
(5)空格由空白、制表符和换行符组成,用以分隔ID、NUM、运算符等,字符分析时被
忽略。
2、单词符号和相应的类别码
假定单词符号和相应的类别码如下:
单词符号种别码
int 1 = 17 float 2 < 20 if 3 <= 21 switch 4 == 22 while 5 != 23 Do 6 > 24
标识符 10 >= 25 整型常量 11 ; 26 + 13 ( 27
- 14 ) 28 * 15 ? 29 / 16 : 30
3、词法分析程序实现的功能
输入:单词序列(以文件形式提供),输出识别的单词的二元组序列到文件和屏幕
输出:二元组构成:(syn,token或sum)
其中: syn 为单词的种别码
token 为存放的单词自身符号串
sum 为整型常数
例:源程序: int ab; float ef=20;ab=10+ef;
输出:
(保留字--1,int) (标识符--10,ab) (分号--26,;)
(保留字--2,float) (标识符--10,ef) (等号--17,=)
(整数--11,20) (分号--26,;) (标识符--10,ab)
(等号--17,=) (整数--11,10) (加号--13,+)
(标识符--10,ef) (分号--26,;)
4、自己准备测试数据存放于TestData.txt文件中,测试数据中应覆盖有以上5种数据,测试结果要求以原数据与结果对照的形式输出并保存在Result.txt中,同时要把结果输出到屏幕。
5、提前准备
①实验前,先编制好程序,上机时输入并调试程序。
②准备好多组测试数据(存放于文件TestData.txt中)。
6、写出实验报告
报告格式:要求有实验名称、实验目的、实验要求、实验内容、实验小结。
其中实验内容包括算法分析、程序流程图及程序代码。
一,系统介绍
本系统使用面象对象语言C#语言编写,对C语言源程序的一个词法分析的程序, 实现界面化,并且可以以手动输入,和导入等两种方式对源程序的输入.
分析得到的分析结果,可以能过手动地保存在本地电脑中,也可以自动保存在”词法分析器\词法分析器\bin\Debug\Text”目录下.并且运行的结果可以在显示器上观看…
分析部分算法:
1.把文本的内容赋值为一个字符串
2.从头到尾扫描这个字串,把它分为两类,一类为标识符,别一类为符号
3.对标识符进行为类,分为关键字,数字,标准标识符和错误的字符串.分类方法为,先将其它为分数字与非数字.是数字则打印数字信息.不是数字则与关键字表对照,有则打印关键字信息,无则再根据标识符的规则判断是否为标识符,是则打印标识符信息,不是则打印错误信息.
4.对符号进行分类,分为运算符和界符,分类方法为用现有的字符串与标准的种别码进行比较,有则打印相应信息,无则打印错误信息.
5.最后统计错误信息,并且保存相应的源文件与分析结果文件
三,调试运行:界面
四,原码:
using System;
using System.Collections.Generic;
using https://www.360docs.net/doc/ad2082474.html,ponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace词法分析器
{
public partial class词法分析器 : Form
{
public string path1 = "", path2 = "";//路径1,路径2
public词法分析器()
{
InitializeComponent();
}
private void ListViewer_Load(object sender, EventArgs e) //Load
{
this.saveFiles.Enabled = false; //控件隐藏
this.button1.Enabled = false;
https://www.360docs.net/doc/ad2082474.html,bel1.Visible = false;
https://www.360docs.net/doc/ad2082474.html,bel2.Visible = false;
this.linkLabel1.Visible = false;
this.linkLabel2.Visible = false;
}
//打开文件的函数
private void openFiles_Click(object sender, EventArgs e)
{
openFileDialog1.InitialDirectory = "";
openFileDialog1.Title = "请选择文件";
openFileDialog1.FileName = "";
openFileDialog1.Filter = "文本文档(*.txt)|*.txt";
if (openFileDialog1.ShowDialog() != DialogResult.Cancel)
{
System.IO.StreamReader sr = new
System.IO.StreamReader(openFileDialog1.FileName);
this.textBox1.Text = sr.ReadToEnd();
sr.Close();
sr.Dispose();
}
https://www.360docs.net/doc/ad2082474.html,bel1.Visible = false; //控件隐藏
https://www.360docs.net/doc/ad2082474.html,bel2.Visible = false;
this.linkLabel1.Visible = false;
this.linkLabel2.Visible = false;
}
//保存源文件的函数
private void saveFiles_Click(object sender, EventArgs e)
{
string stringTemp = this.textBox1.Text;
saveFileDialog1.Title = "请输入要保存的文件名";
saveFileDialog1.Filter = "文本文档(*.txt)|*.txt";
saveFileDialog1.OverwritePrompt = true;
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
path1 = saveFileDialog1.FileName;
StreamWriter sw = new StreamWriter(path1);
sw.Write(stringTemp);
sw.Close();
sw.Dispose();
https://www.360docs.net/doc/ad2082474.html,bel1.Visible = true;
this.linkLabel1.Visible = true;
this.linkLabel1.Text = path1.Substring(https://www.360docs.net/doc/ad2082474.html,stIndexOf("\\") + 1); }
this.saveFiles.Enabled = false;
}
private void button1_Click(object sender, EventArgs e) //保存结果
{
string[] stringTemp = new string[this.resultListBox1.Items.Count]; saveFileDialog1.Title = "请输入要保存的文件名";
saveFileDialog1.Filter = "文本文档(*.txt)|*.txt";
saveFileDialog1.OverwritePrompt = true;
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
path2 = saveFileDialog1.FileName;
StreamWriter sw = new StreamWriter(path2);
for(int counter = 0; counter < this.resultListBox1.Items.Count; counter++) sw.WriteLine(this.resultListBox1.Items[counter].ToString()); sw.Close();
sw.Dispose();
https://www.360docs.net/doc/ad2082474.html,bel2.Visible = true;
this.linkLabel2.Visible = true;
this.linkLabel2.Text = path2.Substring(https://www.360docs.net/doc/ad2082474.html,stIndexOf("\\") + 1); }
this.button1.Enabled = false;
}
private void save()//自动保存结果
{
try
{
https://www.360docs.net/doc/ad2082474.html,bel1.Visible = true; //控件显示
https://www.360docs.net/doc/ad2082474.html,bel2.Visible = true;
this.linkLabel1.Visible = true;
this.linkLabel2.Visible = true;
string stringTemp1 = this.textBox1.Text; //保存源文件
string date = DateTime.Now.ToString("HH-mm-ff");
string zipath1 = "TestData(" + date + ").txt";
path1 = Application.StartupPath + "\\Text\\" + zipath1;
StreamWriter sw1 = new StreamWriter(path1);
sw1.Write(stringTemp1);
this.linkLabel1.Text = zipath1;
sw1.Close();
sw1.Dispose();
string zipath2 = "Result(" + date + ").txt";//保存分析结果
path2 = Application.StartupPath + "\\Text\\" + zipath2;
StreamWriter sw2 = new StreamWriter(path2);
for(int counter = 0; counter < this.resultListBox1.Items.Count; counter++) sw2.WriteLine(this.resultListBox1.Items[counter].ToString());
this.linkLabel2.Text = zipath2;
sw2.Close();
sw2.Dispose();
}
catch
{
MessageBox.Show("保存失败, 请手动保存!", "警告",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
this.saveFiles.Enabled = true;
this.button1.Enabled = true;
https://www.360docs.net/doc/ad2082474.html,bel1.Visible = false; //控件显示
https://www.360docs.net/doc/ad2082474.html,bel2.Visible = false;
this.linkLabel1.Visible = false;
this.linkLabel2.Visible = false;
}
}
//分析函数
private void BeginToAnalyse_Click(object sender, EventArgs e)
{
if (MessageBox.Show("您要在分析完成后,自动保存分析结果吗?", "温馨提示", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
{
//判断列表是否为空,否则清空列表
if (this.resultListBox1.Items.Count != 0)
this.resultListBox1.Items.Clear();
char[] getch = textToCharArray();//字符数组。
//将字符数组转换为词法分析的单词数组。
charArrayToStringArray(getch);
save();//自动保存
this.saveFiles.Enabled = false;
this.button1.Enabled = false;
}
else
{
//判断列表是否为空,否则清空列表
if (this.resultListBox1.Items.Count != 0)
this.resultListBox1.Items.Clear();
char[] getch = textToCharArray();//字符数组。
//将字符数组转换为词法分析的单词数组。
charArrayToStringArray(getch);
this.saveFiles.Enabled = true;
this.button1.Enabled = true;
}
}
private char[] textToCharArray()//将Text中的字符串,存入一个字符数组中。
{
string stringTemp;
stringTemp = this.textBox1.Text;
char[] getch = stringTemp.ToCharArray();//要处理的字符都在getch这个数组中
return getch;
}
// 字符数组到单词数组 //将字符数组转换为字符串数组。
private void charArrayToStringArray(char[] getch)
{
int errorCounter = 0, rowCounter = 1;
//用这个字符串数组存放词法分析后得到的单词。
string[] stringArrange ={ "" };
char charTemp = ' ', charTemp1 = ' ';
// 存放一个分析得到的单词
string stringSave = "";
string[] keywords ={ "auto", "break", "case", "char", "const", "continue", "default", "do","double", "else","enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "signed", "sizeof", "static","struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while" };//关键字32个(46)//标识符(47) //整数(48)
string[] sign = { "~", "+", "+=", "++", "-", "-=", "--", "*", "*=", "?:", "/", "/=", "%", "%=", "&", "&&", "&=", "|", "||", "|=", "!", "!=", "^", "^=", "<", "<=", "<<", "<<=", ">",">=", ">>", ">>=", "=", "==" };//运算符34个(0-33) string[] fengefu = { "(", ")", "[", "]", ",", ";", "{", "}", "'", ".", "#" };//分隔符11个(34-45)
string[] Num ={ "取反", "加", "加等于", "加加", "减", "减等于", "减减", "乘", "乘等于", "条件","除", "除等于", "求余", "求余等于", "按位与", "逻辑与", "按位与等于", "按位或", "逻辑或", "按位或等于", "逻辑非","不等于", "按位异或", "按位异或等于", "小于","小于等于", "左移", "左移等于", "大于", "大于等于", "右移", "右移等于", "等于", "恒等于", "左小括号", "右小括号", "左中括号", "右中括号", "逗号", "分号", "左大括号", "右大括号", "单引号", "点运算符", "#号", "关键字", "标识符", "数字"};//错误所在行-1 try//一次循环得到一个单词。
{
if (getch.Length == 0)
MessageBox.Show("请输入要处理的程序代码......", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
else
{
this.resultListBox1.Items.Add("****************分析开始!");
this.resultListBox1.Items.Add("******************第 " + rowCounter + " 行");//打印第一行
//查找//l为标识符,d为数字,c为".",为1的时候
int l = 0, d = 0, c = 0;
for (int i = 0; i < getch.Length; )
{
charTemp = getch[i];
//标识符或者关键字或者数字
if (char.IsLetter(charTemp) || char.IsNumber(charTemp) || charTemp == '_' || charTemp == '.')
{
if (char.IsLetter(charTemp))//纯字母
l = 1;
else if (char.IsNumber(charTemp))//纯数字
d = 1;
else if (charTemp == '.')//小数点
c = 1;
int a = 0;
//统计标识符或者关键字或者数字的集合
stringSave = stringSave + charTemp.ToString();
try//如果下一字符不是同类
{
charTemp1 = getch[i + 1];
}
catch
{
a = 1;//处理结束
}
if (!(char.IsLetter(charTemp1) ||
char.IsNumber(charTemp1) || charTemp1 == '_' || charTemp1 == '.' ) || a == 1)
{
if (l == 1 && d == 0)//纯字母
{
if (c == 0)//不含"."的字母
{
int flag = 0; //关键字
for (int t = 0; t < keywords.Length; t++)
{
string si = keywords[t];
if (stringSave == si)
{
this.resultListBox1.Items.Add("关键字 , 46 , " + stringSave);
flag = 1;
break;
}
else
flag = 0;
}
if (flag == 0)//标识符
this.resultListBox1.Items.Add("标识符 ,
47 , " + stringSave);
}
else if (c == 1)//含"."的字母
{
//标识符+.+标识符.
string str1 = stringSave.Substring(0, https://www.360docs.net/doc/ad2082474.html,stIndexOf("."));
string str2 =
stringSave.Substring(https://www.360docs.net/doc/ad2082474.html,stIndexOf(".") + 1);
this.resultListBox1.Items.Add("标识符 , 47 , "
+ str1);
this.resultListBox1.Items.Add("点运算符 ,
44 , .");
this.resultListBox1.Items.Add("标识符 , 47 , "
+ str2);
}
}//end if
else if (l == 0 && d == 1)//纯数字
this.resultListBox1.Items.Add("数字 , 47 , " + stringSave);
else if (l == 1 && d == 1)//数字与字母的组合
{
try//数字开头的标识符形如"12c"
{
int shu =
Convert.ToInt32(stringSave.Substring(0, 1));
if (shu >= 0 && shu <= 9)//打印错误信息
{
this.resultListBox1.Items.Add("无效标识符 , -1 , " + stringSave);
errorCounter++;//计算错误个数
}
else
this.resultListBox1.Items.Add("标识符 ,
47 , " + stringSave);
}
catch//不是以数字开头的标识符
{
if (c == 0)//无"."的
this.resultListBox1.Items.Add("标识符 ,
47 , " + stringSave);
else if (c == 1)//有"."的
{
try//"."后面每一个字母是数字
{
int shu =
Convert.ToInt32(stringSave.Substring(https://www.360docs.net/doc/ad2082474.html,stIndexOf(".") + 1, 1));
if (shu >= 0 && shu <= 9)//打印错误信息
{
this.resultListBox1.Items.Add("无
效标识符 , -1 , " + stringSave);
errorCounter++;//计算错误个数
}
else//标识符+.+标识符.
{
string str1 =
stringSave.Substring(0, https://www.360docs.net/doc/ad2082474.html,stIndexOf("."));
string str2 =
stringSave.Substring(https://www.360docs.net/doc/ad2082474.html,stIndexOf(".") + 1);
this.resultListBox1.Items.Add("标
识符 , 47 , " + str1);
this.resultListBox1.Items.Add("点
运算符 , 44 , .");
this.resultListBox1.Items.Add("标
识符 , 47 ," + str2);
}
}
catch//标识符+.+标识符.
{
string str1 = stringSave.Substring(0, https://www.360docs.net/doc/ad2082474.html,stIndexOf("."));
string str2 =
stringSave.Substring(https://www.360docs.net/doc/ad2082474.html,stIndexOf(".") + 1);
this.resultListBox1.Items.Add("标识符 , 47 , " + str1);
this.resultListBox1.Items.Add("点运算
符 , 44 , .");
this.resultListBox1.Items.Add("标识符 , 47 ," + str2);
}//end catch
}//end else if
}//end catch
}//end else if
stringSave = "";//清空单词
l = d = c = 0;//清零
}//end if
}//end if
else//运算符或者分隔符
{
int a = 0;
//统计运算符或者分隔符的集合
stringSave = stringSave + charTemp.ToString();
int flag = 0; //分隔符
//对照分隔符打到则flag = 1;
for (int t = 0; t < fengefu.Length; t++)
{
string si = fengefu[t];
if (stringSave == si) //显示分隔符
{
this.resultListBox1.Items.Add(Num[t + 34] + " , " + (t + 34) + " , " + stringSave);
flag = 1;
break;
}
else
flag = 0;
}
if (flag == 0)//不是分隔符
{
try//如果下一字符不是同类
{
charTemp1 = getch[i + 1];
}
catch
{
a = 1;//处理结束
}
string stringSave2 = charTemp1.ToString();
int flag2 = 0; //对照分隔符 ,查到则flag = 1;
for (int t = 0; t < fengefu.Length; t++)
{
string si = fengefu[t];
if (stringSave2 == si)
{
flag2 = 1;
break;
}
else
flag2 = 0;
}
//下一个字符不是同类则执行操作
if (char.IsLetter(charTemp1) ||
char.IsNumber(charTemp1) || charTemp1 == '_' || charTemp1 == '.' || flag2 == 1 || a == 1)
{
int flag1 = 0; //运算符
//对照运算符打到则flag = 1;
for (int t = 0; t < sign.Length; t++)
{
string si = sign[t];
if (stringSave == si)
{
//显示运算符
this.resultListBox1.Items.Add(Num[t] + " , " + t + " , " + stringSave);
flag1 = 1;
break;
}
else
flag1 = 0;
}
//不是运算符,则为分隔符
if (flag1 == 0 && charTemp != ' ' && charTemp != '\n' && charTemp != '\r')
{
//打印错误信息
this.resultListBox1.Items.Add("无效运算符 ,
-1 , " + stringSave);
errorCounter++;//计算错误个数
}
stringSave = "";//清空单词
}//不是运算符,也不是分隔符,则跳过.
}
else
stringSave = "";//清空单词
}
if (charTemp == '\n') //如果回车,则列表加一行
{
rowCounter++;//行数加1
this.resultListBox1.Items.Add("******************第 "
+ rowCounter + " 行");
}
//回车,换行符,跳过,并且清空单词
if (charTemp == '\n'|| charTemp == '\r'|| charTemp == ' ') stringSave = "";
i = i + 1;//计数器加1
if (i == getch.Length)//循环结束,打印相应信息
{
this.resultListBox1.Items.Add("****************分析结
束!");
if (errorCounter != 0)
MessageBox.Show(" 程序中有 " + errorCounter + " 个错
误!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
else
MessageBox.Show("恭喜您! 程序没有错误!", "信息", MessageBoxButtons.OK, https://www.360docs.net/doc/ad2082474.html,rmation);
}
}//end for
}//end else
}//end try
catch{}
}
//linkLabel1点击
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
System.Diagnostics.Process.Start(path1);
}
//linkLabel2点击
private void linkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
System.Diagnostics.Process.Start(path2);
}
//打开种别码对照表
private void button2_Click(object sender, EventArgs e)
{
string path = Application.StartupPath + "\\Text\\种别码对照表.doc"; System.Diagnostics.Process.Start(path);
}
//关闭文件
private void close_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
五,运行结果:
TestData(16-44-17).txt源文件:
void main()
{
a.3
if(a12>12)
abc=13a;
else
abc=12;
}
123. 123e 123E
Result(16-44-17).txt结果:
****************分析开始!
******************第 1 行
关键字 , 46 ,void
标识符 , 47 ,main
左小括号 , 34 , (
右小括号 , 35 , )
******************第 2 行
左大括号 , 40 , {
******************第 3 行
标识符 , 47 ,a.3
******************第 4 行
关键字 , 46 ,if
左小括号 , 34 , (
标识符 , 47 ,a12
大于 , 28 , >
数字 , 47 ,12
右小括号 , 35 , )
******************第 5 行
标识符 , 47 ,abc
等于 , 32 , =
无效标识符: 13a 错误!
分号 , 39 , ;
******************第 6 行
关键字 , 46 ,else
******************第 7 行
标识符 , 47 ,abc
等于 , 32 , =
数字 , 47 ,12
分号 , 39 , ;
******************第 8 行
右大括号 , 41 , }
******************第 9 行
数字 , 47 ,123.
无效标识符: 123e 错误!
无效标识符: 123E 错误!
****************分析结束!