一、MIDL的词法规则
1. 语言的关键字(16个):
struct | float | boolean | short | long |
---|---|---|---|---|
double | int8 | int16 | int32 | int64 |
uint8 | uint16 | uint32 | uint64 | char |
unsigned |
注:关键字是保留字,并且不区分大小写。
2. 语言的专用符号17个:
{ | } | ; | [ | ] |
---|---|---|---|---|
* | + | - | ~ | / |
% | >> | << | & | ^ |
| | , |
3. 标示符ID、整数INTEGER、 字符串STRING和BOOLEAN等4个词法规则(标黑的)通过下列正则表达式定义:
ID = LETTER (UNDERLINE?( LETTER | DIGIT))*
LETTER = [a-z] | [A- Z]
DIGIT = [0-9]
UNDERLINE= _
INTEGER = (0 | [1-9] [0-9]*) INTEGER_TYPE_SUFFIX?
INTEGER_TYPE_SUFFIX = l | L
STRING = " ( ~\ | ~" )* "
BOOLEAN = TRUE | FALSE
4. 补充说明的注意事项
其中,正则符号标识含义如下表:
~\ | ~" | 表示匹配除了 \ ,"外所有字符 |
---|---|
[a-z] | 表示a字符到z字符,与课件ppt中含义一致 |
? | 表示匹配 0 或 1 次,与课件ppt中含义一致 |
+ | 表示匹配 1 或 多次,与课件ppt中含义一致 |
* | 表示匹配 0 或 多次,与课件ppt中含义一致 |
a | b | 表示匹配 a字符或b字符,与课件ppt中含义一致 |
( ) | 表示一个组,比如()*表示*对括号内组整体起作用,与课件ppt中含义一致 |
空白、换行符和制表符在词法分析时可以忽略掉,但是字符串本身的空白、换行符和制表符不能忽略。
二、词法分析程序课程设计要求
词法分析程序的设计与实现需要按照以下要求完成。
- 基于词法规则设计词法分析器(20分)
画出确定的有穷自动机(确定化),并提供必要的文字说明。提交状态转换图.doc- 词法分析程序的编程实现与测试(80分)
- 编程实现词法分析器,提交可执行词法分析程序的源程序**(语言不限,60分)**
编程实现词法分析程序,该词法分析程序能够读取测试输入文件中的源程序,并将其词法分析的结果即token序列或词法分析的所有错误信息输出到tokenOut.txt中,以便于检查你的词法分析程序的正确性。输出的错误信息要包括错误所在行数,错误类型等信息(同目录下有测试文件夹,共五大类测试输入)。- 提交所生成的词法分析程序的测试方案(20分).doc
注:将状态转换图.doc、源程序、以及词法分析程序测试方案.doc文件放在一个文件夹下,按照“班级_学号_姓名”的命名方式打包提交。如果还有其它需要说明的问题须写在readme.doc中。
三、有关附加分的说明
关于字符串,如果词法分析程序考虑转义字符(正规表达式的定义如下),则作为加分项目,可以选做。即如果词法分析程序考虑了对转义字符的支持,额外加分10分,但总分不得超过100分。例如,根据词法分析程序的完成情况,某同学取得92分的成绩,但由于该同学的词法分析程序正确处理了转义字符(必须在状态转换图、词法分析源程序和readme.doc中分别进行备注),该同学的总分合计为100分。
STRING = " (ESCAPE_SEQUENCE | (~\ | ~") )* "
ESCAPE_SEQUENCE = \ ( b | t | n | f | r | " | \ )
四、确定的有穷自动机
五、MIDL词法分析程序(C++)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <iomanip>
using namespace std;
//设置保留字,不区分大小写!!
string keyWord[] = { "struct","float","boolean",
"short","long","double","int8","int16","int32",
"int64","uint8","uint16","uint32","uint64","char",
"unsigned" };
char separator[] = {'{', '}', ';', '(', ')','[',']', ','}; //分隔符
char singleOperator[] = {'*', '+', '-', '~', '/', '%','&','^', '|'}; //单目运算符
string doubleOperator[] = { ">>","<<" }; //双目运算符
char filter[] = {' ','\t','\r','\n'}; //过滤符
int lineno = 1; //记录当前字符所在行数
/**判断是否为过滤符**/
bool isFilter(char ch) {
for (int i = 0; i < strlen(filter); i++) {
if (ch == filter[i]) {
return true;
}
}
return false;
}
/**判断是否为关键字**/
bool isKeyWord(string word) {
//将单词中的大写字母转换为小写字母
for (int i = 0; i < word.length(); i++) {
if (word[i] >= 'A' && word[i] <= 'Z') {
word[i] += 'a' - 'A';
}
}
//cout << word << endl;
for (int i = 0; i < 16; i++) {
if (keyWord[i] == word) {
return true;
}
}
return false;
}
/**判断是否为字母**/
bool isLetter(char ch) {
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
return true;
else
return false;
}
/**判断是否为数字**/
bool isDigit(char ch) {
if (ch >= '0' && ch <= '9')
return true;
else
return false;
}
/**判断是否为BOOLEAN类型**/
bool isBoolean(string word) {
if (word == "TRUE" || word == "FALSE") {
return true;
}
else {
return false;
}
}
/**判断是否为单目运算符**/
bool isSingleOperator(char ch) {
switch (ch) {
case '*':
return true;
case '+':
return true;
case '-':
return true;
case '~':
return true;
case '/':
return true;
case '%':
return true;
case '&':
return true;
case '^':
return true;
case '|':
return true;
}
return false;
}
/**判断是否为分隔符**/
bool isSeparator(char ch) {
switch (ch) {
case '{':
return true;
case '}':
return true;
case ';':
return true;
case '(':
return true;
case ')':
return true;
case '[':
return true;
case ']':
return true;
case ',':
return true;
}
return false;
}
void analyse(string line,string writefilename) {
ofstream out(writefilename, ios::app); //app表示每次都定位到文件末尾
if (out.fail()) {
cout << "写入文件打开失败!" << endl;
}
//line += "";
int i = 0;
char ch = line[i];
string arr = "";
i = 1;
int outlen = 40;
while (i <= line.length()) {
//ch = line[i];
arr = "";
if (isFilter(ch)) { //判断是否为过滤符
ch = line[i++];
}
else if (ch == '_') {
while (isLetter(ch) || isDigit(ch) || ch == '_') { //如果字符是字母、数字或下划线
arr += ch;
ch = line[i++]; //读取下一个字符
}
out << endl << "第" << lineno << "行: " << arr << "\t非法的标识符ID!" << endl << endl; //打印错误信息
}
else if(isLetter(ch)){//如果单词的首字符是字母
while (isLetter(ch)||isDigit(ch)||ch=='_') { //如果字符是字母、数字或下划线
arr += ch;
ch = line[i++]; //读取下一个字符
}
if (isKeyWord(arr)) {
//token_type = 1; //单词类型为关键字
out<< arr << setw(outlen -arr.length())<< "关键字" << endl;
}
else if(isBoolean(arr)){
out<<arr<< setw(outlen - arr.length())<<"BOOLEAN"<<endl;
}
else {
if (arr[arr.length() - 1] != '_') {//如果末尾不是下划线
out << arr << setw(outlen - arr.length())<<"标识符ID" << endl;
}
else {
out <<endl<< "第" << lineno << "行: "<<arr<<"\t标识符末尾不能是下划线!" <<endl<<endl; //打印错误信息
//break;
}
}
}
else if (isDigit(ch)) { //判断是否为数字
while (isDigit(ch)) {
arr += ch;
ch = line[i++]; //读取下一个字符
}
if (ch == 'l' || ch == 'L') { //如果末尾有l或L
arr += ch;
ch = line[i++]; //读取下一个字符
if (arr.length() > 2 && arr[0] == '0') {//判断第一个数字是否为0
out <<endl<< "第" << lineno << "行: "<<arr<<"第一个数字不能为零!" << endl<<endl; //打印错误信息
//break;
}
}
//如果末尾没有l或L
else if (arr.length() > 1 && arr[0] == '0') {//判断第一个数字是否为0
out <<endl<< "第" << lineno << "行: "<<arr<<"第一个数字不能为零!" << endl<<endl; //打印错误信息
//break;
}
else {
out << arr << setw(outlen - arr.length())<<"整数INTEGER" << endl;
}
}
else if (isSingleOperator(ch)) {
arr += ch;
ch = line[i++];
out << arr << setw(outlen - arr.length())<< "单目运算符" << endl;
}
else if (isSeparator(ch)) {
arr += ch;
ch = line[i++];
out << arr << setw(outlen - arr.length())<<"分隔符" << endl;
}
else if (ch == '>') { //判断是否为双目运算符>>
arr += ch;
ch = line[i++]; //读取下一个字符
if (ch == '>') {
arr += ch;
ch = line[i++]; //读取下一个字符
out << arr << setw(outlen - arr.length())<<"双目运算符" << endl;
}
else {
out <<endl<< "第" << lineno << "行: "<<arr<<"\t为非法符号!" << endl<<endl; //打印错误信息
//break;
}
}
else if (ch == '<') { //判断是否为双目运算符<<
arr += ch;
ch = line[i++]; //读取下一个字符
if (ch == '<') {
arr += ch;
ch = line[i++]; //读取下一个字符
out << arr << setw(outlen - arr.length())<<"双目运算符" << endl;
}
else {
out << endl << "第" << lineno << "行: " << arr << "\t为非法字符!" << endl << endl; //打印错误信息
//break;
}
}
else if (ch == '\"') { //判断是否为字符串
arr += ch;
char bfch = ch;
ch = line[i++];
bool flag = 1; //是否合法
while (i <= line.length() && !(ch == '\"' && bfch !='\\')){
/*
* 考虑了转义字符!!!
*/
if (ch == '\\') {
arr += '\\';
bfch = ch;
ch = line[i++];
if (ch == 'b' || ch == 't' || ch == 'n' || ch == 'f' || ch == 'r') {
arr += ch;
bfch = ch;
ch = line[i++];
}
else if (ch == '\"' ) {
arr += '\"';
bfch = ch;
ch = line[i++];
}
else if (ch == '\\') {
arr += '\\';
bfch = ch;
ch = line[i++];
}
else {
flag = 0; //字符串不合法
}
}
else {
arr += ch;
bfch = ch;
ch = line[i++];
}
}
arr += ch;
ch = line[i++];
if (flag) {
//printf("%s\t\t字符串STRING\n", arr.c_str());
out <<arr<< setw(outlen - arr.length())<<"字符串STRING"<< endl;
}
else {
out << endl << "第" << lineno << "行: \t字符串"<<arr<<"中存在非法字符!" << endl << endl; //打印错误
}
}
else{
out <<endl<< "第" << lineno << "行:\t"<<ch<<"\t无法识别的字符!" <<endl<< endl; //打印错误信息
break;
}
}
out.close(); //关闭文件输出流
}
void run(string filename,string writefile) {
ofstream out(writefile, ios::trunc); //清空文件内容
out.close(); //关闭文件输出流
ifstream in(filename);
string line = "";
if (in) {//如果有该文件
lineno = 1; //记录当前字符所在行数
cout << "=====================MIDL词法分析程序开始===================" << endl<<endl;
cout<<"正在分析\""<<filename<<"\"文件"<<endl<<endl;
while (getline(in, line)) { //读取一行
cout << "正在分析第"<<lineno<<"行……" << endl;
analyse(line,writefile); //对这一行进行词法分析
lineno++; //所在行数加1
}
cout << endl<<"解析结果已写入\""<<writefile<<"\"文件中!" << endl<<endl;
cout << "=====================MIDL词法分析程序结束===================" << endl<<endl<<endl<<endl;
}
else { //如果没有该文件,则打印报错
cout << "文件不存在!" << endl;
}
in.close();
}
int main() {
//此处可以更改源程序文件
string read[] = {"test_1_kw.txt","test_2_kw.txt","test_3_formula.txt","test_4_identifier.txt",
"test_5_identifier_with_error.txt","test_6_identifier_with_error.txt",
"test_7_expression.txt","test_8_complex.txt","test_9_complex_with_escape.txt"};
//此处可以修改分析后生成的文件名
string write[] = { "tokenOut1.txt" ,"tokenOut2.txt" ,"tokenOut3.txt" ,"tokenOut4.txt" ,
"tokenOut5.txt","tokenOut6.txt","tokenOut7.txt","tokenOut8.txt","tokenOut9.txt" };
for (int i = 0; i < 9; i++) { //遍历测试所有文件
run(read[i],write[i]);
}
return 0;
}