【转载】写的C#编写的词法分析器
带有可视化界面 感谢原作者
用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、写出实验报告
报告格式:要求有实验名称、实验目的、实验要求、实验内容、实验小结。
其中实验内容包括算法分析、程序流程图及程序代码。
种别码
类型 | 名称 | 各别码 | 符号 | 名称 | 各别码 | 符号 |
运算符 | 取反 | 1 | ~ | 按位或 | 18 | | |
加 | 2 | + | 逻辑或 | 19 | || | |
加等于 | 3 | ++ | 按位或等于 | 20 | |= | |
加加 | 4 | ++ | 逻辑非 | 21 | ! | |
减 | 5 | - | 不等于 | 22 | != | |
减等于 | 6 | -= | 按位异或 | 23 | ^ | |
减减 | 7 | -- | 按位异或等于 | 24 | ^= | |
乘 | 8 | * | 小于 | 25 | < | |
乘等于 | 9 | *= | 小于等于 | 26 | <= | |
条件 | 10 | ?: | 左移 | 27 | << | |
除 | 11 | / | 左移等于 | 28 | <<= | |
除等于 | 12 | /= | 大于 | 29 | > | |
求余 | 13 | % | 大于等于 | 30 | >= | |
求余等于 | 14 | %= | 右移 | 31 | >> | |
按位与 | 15 | & | 右移等于 | 32 | >>= | |
逻辑与 | 16 | && | 等于 | 33 | = | |
按位与等于 | 17 | &= | 恒等于 | 34 | == | |
分隔符 | 左小括号 | 35 | ( | 左大括号 | 41 | { |
右小括号 | 36 | ) | 右大括号 | 42 | } | |
左中括号 | 37 | [ | 单引号 | 43 | ‘ | |
右中括号 | 38 | ] | 点运算符 | 44 | . | |
逗号 | 39 | , | #号 | 45 | # | |
分号 | 40 | ; | 错误代码 | -1 | ---- | |
关键字 | 关键字 | 46 | ---- | |||
标识符 | 标识符 | 47 | l(l|d)* | |||
数字 | 数字 | 48 | d d* | |||
错误代码 | 错误代码 | -1 | ---- |
源代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
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;
this.label1.Visible = false;
this.label2.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();
}
this.label1.Visible = false; //控件隐藏
this.label2.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();
this.label1.Visible = true;
this.linkLabel1.Visible = true;
this.linkLabel1.Text = path1.Substring(path1.LastIndexOf("\\") + 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();
this.label2.Visible = true;
this.linkLabel2.Visible = true;
this.linkLabel2.Text = path2.Substring(path2.LastIndexOf("\\") + 1);
}
this.button1.Enabled = false;
}
private void save()//自动保存结果
{
try
{
this.label1.Visible = true; //控件显示
this.label2.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;
this.label1.Visible = false; //控件显示
this.label2.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, stringSave.LastIndexOf("."));
string str2 = stringSave.Substring(stringSave.LastIndexOf(".") + 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(stringSave.LastIndexOf(".") + 1, 1));
if (shu >= 0 && shu <= 9)//打印错误信息
{
this.resultListBox1.Items.Add("无效标识符 , -1 , " + stringSave);
errorCounter++;//计算错误个数
}
else//标识符+.+标识符.
{
string str1 = stringSave.Substring(0, stringSave.LastIndexOf("."));
string str2 = stringSave.Substring(stringSave.LastIndexOf(".") + 1);
this.resultListBox1.Items.Add("标识符 , 47 , " + str1);
this.resultListBox1.Items.Add("点运算符 , 44 , .");
this.resultListBox1.Items.Add("标识符 , 47 ," + str2);
}
}
catch //标识符+.+标识符.
{
string str1 = stringSave.Substring(0, stringSave.LastIndexOf("."));
string str2 = stringSave.Substring(stringSave.LastIndexOf(".") + 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, MessageBoxIcon.Information);
}
}//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();
}
}
}