C++实现词法分析器详解

词法分析器是干么用的?

法分析器就是将字符序列转化为单词序列的程序。举个例子来讲:

现在有一行代码:

sum = sum + 3;

那么经过词法分析后,就将此行代码拆分为了:

sum

标识符

   =

赋值操作符

sum

标识符

    +

 运算符

  321

 数字

    ;

语句结束

当然实际上的词法分析是很复杂的,为了方便理解:介绍下用到的知识:

1.字母表:

对于词法分析来说,我们要做的就是将语句分析称单词形式,那么我们就需要规定我们的程序到底能够识别哪些字符。而这些字符就构成了词法分析的字母表:

1、  标识符:字母打头,后接任意字母或数字。

2、  保留字:标识符的子集,包括:if,else,for,while,do,int,write,read。

3、无符号整数:由数字组成,但最高位不能为0,允许一位的0。

4、分界符:(、)、;、{、}

5、运算符:+、-、*、/、=、<、>、>=、<=、!=、==

6、注释符:/*       */


以上6种就是我们所能够识别到的 字符集合。那么字母表有什么用呢?

接着上面的例子来讲:对于sum = sum+3; 我们如何能将其拆分呢?就是因为s 、u、m、= 、+、3、; 这些符号在字母表里,所以我们才能够识别到出现的字符,并进行词法分析。


状态图:

词法分析中,有很多种状态。首先说一下为什么会存在这些状态:

还是上面的例子:sum = sum + 3; 首先我们为什么能将这条语句拆分成: sum、 = 、+、3、; 这几个单词呢?当我们扫描到s 时,接着扫描u,然后扫描m,扫描到‘=’号时由于 = 所代表的状态和 s u m 所代表的状态是不一样的。所以就将sum归为一个单词,当然 扫描完s 后可以接着扫描 u 和m 是因为它们三者的所代表状态是一样的。

然后从 ‘=’接着往下扫描。

接着看词法分析的总状态图




我先详细介下这个状态图:

S:为初态。即开始状态。

S->I:S状态时输入1到9 这些数字后,进入I状态

I->I:I状态时,输入1-9这些数字后还是I状态。

S->H:表示S状态时,输入0后得到的状态(因为在数中0不能出现在首位:013是不允许的。但是允许单独的0,所以将0单独作为一个状态

S->T:S状态时,输入a - z     A-Z的字母得到的状态T。如此时还是输入字母,则当前状态不变

S->J:S状态时,输入分界符得到状态J。

S->F:S状态时输入!进入状态F(非)

F->G:F状态时,输入=进入G状态,即:!=(不等于)

S->G:S状态时,输入+ 或者 - 或者* 时得到状态G。

S->B:S状态时,输入< > = 进入B状态

B->G:B状态时,输入 = 得到G状态:(==)等于

S->A:S状态时输入 / 得到A状态,因为/可以表示为注释符的/ 或者除号

A->C:A状态时输入 * 得到C状态 ,即:注释

C->C:C状态时,输入除了 *的其他字符得到的还是 C状态,因为分解符结束时是*/ 那么其他字符肯定是注释内容

C->D:C状态时,输入* 得到D状态,因为输入*后,我们要判断它后面是不是/ 如果是,则说明注释结束,如果不是则还是注释的一部分

D->C:D状态时,输入*时可能后面输入的是/ 或者其他字符,则需要分别判断可能性,如果还是注释的一部分,则返回C

D->D:因为不知道*后面是不是/ ,则在此需要判断。

D->E:如果 * 后面是/ 则注释结束,进入E状态。


3.有穷自动机

状态图大概说完了,那么我们此时就可以根据状态图构造有穷自动机了。

什么是有穷自动机:比如现在给你个状态,给输入内容,那么程序就可以根据有穷自动机来自动的进行词法分析了。

说白了又穷自动的创建方式就是一个邻接矩阵:

举个例子:

状态\字母表  

           A

       1

             )

 

 

 

 

 

 

        1

           2

 

 

 

 

 

 

 

 

        2

                          

                

 

                      

                    

 

 

 

 

        3

 

        6

                       

 

 

 

 

 

 

        4

 

 

 

 

 

 

 

 

 

        5

 

 

 

 

 

 

 

 

 

        6

 

 

 

 

 

 

 

 

 

        7

 

 

 

 

 

 

 

 

 

比如现在我们处于1状态,得到的输入时字母A,那么输入A后状态变为2状态,那么我们就将1状态输入A得到的2状态存储在【1】【A】中。当然如果

得不到其他的状态,则说明当前的分词结束了,说的脑残点:这也就是为什么 sum 分词为sum 了。因为s 、u、 m之间通路,而通过m到达 = 不是通路的。


学过DFS和BFS的筒子们应该能够很快的明白这种方式,因为这跟在图中找路径很相似。

东西都介绍完了。

那么上代码:

首先我们需要一个字母表类来存储我们所能识别的符号:

#pragma once
class CCharacterTable          
{

private:
	enum
	{
		MAX = 128
	};
	char m_character[MAX];  //字符数组
	int m_index[MAX];         //字符在字符数组中存取的位置    
	int m_number;             //已经存了多少个字符

public:
	CCharacterTable();
	~CCharacterTable(); 
	void Init();                                //初始化字母表
	void AddCharcter(const char& character);    //添加字符 
	int GetLength() const{ return m_number; }      //得到字母表的长度
	bool isInclude(const char &character) const{ return m_index[(int)character] != -1; }  //字母表中是否含有此字符
	int GetIndexByCharacter(const char& character) const;    //    
	char GetCharacterByIndex(const int& index) const { return m_character[index]; }//得到某个下标存储的字符

	

};
字母表实现:
#include "stdafx.h"
#include "CharacterTable.h"


CCharacterTable::CCharacterTable()
{
	Init();
}

void CCharacterTable::Init()
{
	for (int i = 0; i < MAX; i++)
	{
		m_character[i] = -1;
		m_index[i] = -1;
	}
}

CCharacterTable::~CCharacterTable()
{
}

void CCharacterTable::AddCharcter(const char& character)
{
	if (GetIndexByCharacter(character) == m_number)
	{
		m_index[(int)character] = m_number;
		m_character[m_number++] = character;
	}

}

int CCharacterTable::GetIndexByCharacter(const char& character) const
{
	if (m_index[(int)character] == -1)
	{
		return m_number;
	}
	else
	{
		return m_index[(int)character];
	}
}

这里用到了一个index 数组和character数组,用来简化查找当前字符是否已经存入的操作,比如:字符A存入character数组的第三个位置,那么Index[(int )A] 存的是字符在character的位置,这样方便查找。


状态类:

 

#pragma once
class CState
{
private:

public:
	CState();
	CState(const std::string& name, bool isEndState);  
        std::string GetName(){ return  m_name; }       //得到状态的名字
	bool GetisEndState(){ return m_isEndState; }     //状态是否为终态
	bool operator==(const CState& state) const{ return this->m_name == state.m_name; } //运算符重载 状态和状态的相等判定
	~CState();

	std::string m_name;          //状态的名字
	bool m_isEndState;           //状态是否为终态

	
};
实现:

#include "stdafx.h"
#include "State.h"

CState::CState()
{
	m_name = "no name";
	m_isEndState = false;
}

CState::CState(const std::string& name, bool isEndState)
{
	m_name = name;
	m_isEndState = isEndState;
}

CState::~CState()
{
}
状态类:包括了每个状态的名字和是否为终态。

那么有了状态类后我们就要构造我们的状态集了:

状态集:

#pragma once

#include"State.h"

class CStateTable
{
private:
	std::vector<CState> m_stateSet;

public:
	CStateTable();
	~CStateTable();

	void Init();                  //状态集的初始化
	void AddState(const std::string& name, bool isEndState);  //添加状态
	int GetIndexByStateName(const std::string &name) const;  //通过下标得到该状态
	int GetIndexByState(const CState& state) const;                //得到状态的序号
	CState GetStateByIndex(const int& index) const{ return m_stateSet[index]; } //得到某个下标的状态
	CState GetStateByname(const std::string& name) const; //通过名字得到状态
	int GetLength() const{ return m_stateSet.size(); } //返回状态集有多少元素

};
实现:
#include "stdafx.h"
#include "StateTable.h"
#include"State.h"

CStateTable::CStateTable()
{
	Init();
}


CStateTable::~CStateTable()
{
}

void CStateTable::AddState(const std::string& name, bool isEndState)
{
	if (GetIndexByStateName(name) == -1)
	{
		m_stateSet.push_back(CState(name, isEndState));
	}
}

int CStateTable::GetIndexByStateName(const std::string& name) const
{
	int ans = 0;
	for (std::vector<CState>::const_iterator it = m_stateSet.begin(); it != m_stateSet.end(); it++)
	{
		if (it->m_name == name)
		{
			return ans;
		}
		ans++;
	}
	return -1;
}

int CStateTable::GetIndexByState(const CState& state) const
{
	int ans = 0;
	for (std::vector<CState>::const_iterator it = m_stateSet.begin(); it != m_stateSet.end(); it++)
	{
		if ((*it) == state)
		{
			return ans;
		}
		ans++;
	}
	return -1;
}



CState CStateTable::GetStateByname(const std::string& name) const
{
	for (std::vector<CState>::const_iterator it = m_stateSet.begin(); it != m_stateSet.end(); it++)
	{
		if (it->m_name == name)
		{
			return (*it);
		}
	}

	return m_stateSet[0];
}

void CStateTable::Init()
{
	m_stateSet.clear();
	AddState("Wrong", false);
	AddState("Start", false);
}

根据状态集建DFA:

#pragma once
#include"ConversionTable.h"
class CDFA
{

private:

	CStateTable m_stateTable;  //状态集
	CCharacterTable m_characterTable;    //字母表
	CConversionTable m_conversionTable; //转换表
	CState m_nowState; //当前的状态
	CState m_startStart; //开始状态

public:
	CDFA();
	~CDFA();
	void Init();    //初始化
	void InitCharacterTable(); //初始化字母表
	void InitState();     //初始化状态集
	void InitConversionTable(); //初始化状态表

	CStateTable& GetStateTable(){ return m_stateTable; }    //取得状态集
	CCharacterTable& GetCharacterTable(){ return m_characterTable; }         //取得字母表
	CConversionTable& GetConversionTable(){ return m_conversionTable; }           //取得转换表

	CState ConversionState(const char& character);      //得到当前字母的状态 
	CState GetnowState()const{ return m_nowState; }
	
	bool isIncludeCharacter(const char& character) const{ return m_characterTable.isInclude(character);} //是否含有该字符
	CState ReStart(){ return m_nowState = m_startStart; } //重新开始


};


实现:
#include "stdafx.h"
#include "DFA.h"


CDFA::CDFA()
{
	m_nowState = m_startStart = CState("Start", false);
}


CDFA::~CDFA()
{
}

void CDFA::Init()
{
	InitCharacterTable();
	InitState();
	InitConversionTable();
	
}

void CDFA::InitConversionTable()               //构造转换表
{
	m_conversionTable.Init(m_characterTable, m_stateTable);

	for (int i = '1'; i <= '9'; ++i)
		m_conversionTable.AddConversion("Start", "Num_1_9", (char)i);  //意思开始状态时,输入'1' --- '9'的话进入 Num_1_9状态, 以下类似。

	for (int i = '0'; i <= '9'; ++i)
		m_conversionTable.AddConversion("Num_1_9", "Num_1_9", (char)i); //

	m_conversionTable.AddConversion("Start", "Num_0", '0');

	for (int i = 'a'; i <= 'z'; ++i)
		m_conversionTable.AddConversion("Start", "Letter", (char)i);

	for (int i = 'A'; i <= 'Z'; ++i)
		m_conversionTable.AddConversion("Start", "Letter", (char)i);

	for (int i = '0'; i <= '9'; ++i)
		m_conversionTable.AddConversion("Letter", "Letter", (char)i);

	for (int i = 'a'; i <= 'z'; ++i)
		m_conversionTable.AddConversion("Letter", "Letter", (char)i);

	for (int i = 'A'; i <= 'Z'; ++i)
		m_conversionTable.AddConversion("Letter", "Letter", (char)i);


	m_conversionTable.AddConversion("Start", "Space_1", ' ');
	m_conversionTable.AddConversion("Space_1", "Space_1", ' ');
	m_conversionTable.AddConversion("Start", "Delimiter", '(');
	m_conversionTable.AddConversion("Start", "Delimiter", ')');
	m_conversionTable.AddConversion("Start", "Delimiter", ';');
	m_conversionTable.AddConversion("Start", "Delimiter", '{');
	m_conversionTable.AddConversion("Start", "Delimiter", '}');
	m_conversionTable.AddConversion("Start", "Operator_2", '!');
	m_conversionTable.AddConversion("Operator_2", "Operator_4", '=');
	m_conversionTable.AddConversion("Start", "Operator_4", '+');
	m_conversionTable.AddConversion("Start", "Operator_4", '-');
	m_conversionTable.AddConversion("Start", "Operator_4", '*');
	m_conversionTable.AddConversion("Start", "Operator_3", '>');
	m_conversionTable.AddConversion("Start", "Operator_3", '<');
	m_conversionTable.AddConversion("Start", "Operator_3", '=');
	m_conversionTable.AddConversion("Operator_3", "Operator_4", '=');
	m_conversionTable.AddConversion("Start", "Operator_1", '/');
	m_conversionTable.AddConversion("Operator_1", "Annotator_1", '*');
	m_conversionTable.AddConversion("Annotator_1", "Annotator_2", '*');
	m_conversionTable.AddConversion("Annotator_2", "Annotator_2", '*');
	m_conversionTable.AddConversion("Annotator_2", "Annotator_3", '/');
	for (int i = 32; i <= 125; i++)
	{
		if ((char)i != '*')
		{
			m_conversionTable.AddConversion("Annotator_1", "Annotator_1", (char)i);
		}
	}
	for (int i = 32; i <= 125; i++)
	{
		if ((char)i != '/')
		{
			m_conversionTable.AddConversion("Annotator_2", "Annotator_1", (char)i);
		}
	}
}

void CDFA::InitCharacterTable()               //构建字母表
{
	for (int i = 'a'; i <= 'z'; ++i)
		m_characterTable.AddCharcter((char)i);
	for (int i = 'A'; i <= 'Z'; ++i)
		m_characterTable.AddCharcter((char)i);
	for (int i = '0'; i <= '9'; ++i)
		m_characterTable.AddCharcter((char)i);
	m_characterTable.AddCharcter('(');
	m_characterTable.AddCharcter(')');

	m_characterTable.AddCharcter('{');
	m_characterTable.AddCharcter('}');

	m_characterTable.AddCharcter(';');

	m_characterTable.AddCharcter('+');
	m_characterTable.AddCharcter('-');
	m_characterTable.AddCharcter('*');
	m_characterTable.AddCharcter('/');

	m_characterTable.AddCharcter('=');
	m_characterTable.AddCharcter(' ');
	m_characterTable.AddCharcter('!');
	m_characterTable.AddCharcter('>');
	m_characterTable.AddCharcter('<');
}
void CDFA::InitState()                //初始化状态
{
	m_stateTable.AddState("Num_1_9", true);  //I
	m_stateTable.AddState("Num_0", true);     //H
	m_stateTable.AddState("Letter", true);        //T
	m_stateTable.AddState("Delimiter", true);   // J
	m_stateTable.AddState("Operator_1", true);    // A 即:/
	m_stateTable.AddState("Operator_2", false); //F
	m_stateTable.AddState("Operator_3", true);  //B
	m_stateTable.AddState("Operator_4", true);  //G
	m_stateTable.AddState("Annotator_1", false);  //C
	m_stateTable.AddState("Annotator_2", false);   //D
	m_stateTable.AddState("Annotator_3", true);   //E
	m_stateTable.AddState("Space_1", true);  //空格
}

CState CDFA::ConversionState(const char& character)
{
	return m_nowState = m_conversionTable.GetNextState(m_nowState, character);
}
开始分析程序的编写:

#pragma once
#include"DFA.h"
class CAanlysisManager
{
private:
	CDFA m_dfa;
	FILE *m_infile;
	FILE *m_outfile;
	enum
	{
		MAX = 1024
	};
	int m_line;
//以下是为了存放拆分后的单词的类别
	std::vector<std::string> m_num; // 数字
	std::vector<std::string> m_delimiter; //分界符
	std::vector<std::string> m_operator; //操作符
	std::vector<std::string> m_keyWord;//关键字
	std::vector<std::string> m_errorLog; //错误日志
	std::vector<std::string> m_annotations; //注释
	std::vector<std::string> m_identifier; // 标识符
	
	

public:
	CAanlysisManager();
	~CAanlysisManager();
	bool Open(const char *address); //打开文件
	void RunAnalysis();       //词法分析
	void AddError(std::string content, char PreEndState_Characterchar, CState state, bool isFind); //错误日志的添加
	void ShowError(); //显示错误状态
	void Push(const std::string& word, CState state); //加入单词属于的库(vector)
	void CloseFile(); //关闭文件
};
实现:
#include "stdafx.h"
#include "AanlysisManager.h"


CAanlysisManager::CAanlysisManager()
{
	m_dfa.Init();  //将字符表和状态集合初始化
	m_line = 0;
}


CAanlysisManager::~CAanlysisManager()
{
	
}

bool CAanlysisManager::Open(const char* address)  //读取文件
{
	

	m_infile = fopen(address, "r");
	if (m_infile != NULL)
	{
		m_line = 0;
		
		m_outfile = fopen(".\\lexical.txt", "w");
		return true;
	}
	std::cout << "文件读取失败" << std::endl;
	return false;
}

void CAanlysisManager::CloseFile()             //关闭文件
{
	fclose(m_infile);
}

void CAanlysisManager::AddError(std::string content, char PreEndState_Characterchar, CState state, bool isFind = true)   //添加错误输出
{
	std::stringstream temp;
	std::string hint;
	temp << m_line;
	if (!isFind)
	{
		temp >> hint;
		hint = "Line " + hint + " :未知字符" + PreEndState_Characterchar;
		m_errorLog.push_back(hint);
	}
	if (content.length() == 0)return;
	hint.clear();
	temp >> hint;
	if (state.m_name == "Operator_2")
	{
		hint = "Line " + hint + " :无法识别的运算符" + content;
	}
	else if (state.m_name == "Annotator_1" || state.m_name == "Annotator_2")
	{
		hint = "Line " + hint + " :不完整的注释" + content;
	}
	temp >> hint;
	m_errorLog.push_back(hint);
}


void CAanlysisManager::RunAnalysis()
{
	char strline_char[MAX];
	std::string strline_str = "";
	std::string strline_rest = "";
	int preCoutStr_EndCharacter, PreEndState_Character; //上一个输出的字符串的尾字符位置,上一个被识别到的终结符的字符位置
	preCoutStr_EndCharacter = PreEndState_Character = -1;
	CState nowState = m_dfa.GetnowState();  
	CState preCharacterState; //上一个字符的状态
	CState preEndState; //上一个终结状态
	bool isFindEndState = false;
	while (!feof(m_infile))
	{
		m_line++;
		fgets(strline_char, MAX, m_infile);
		strline_str = strline_rest + std::string(strline_char, 0, strlen(strline_char) - 1);
		for (auto i = strline_rest.length(); i < strline_str.length(); ++i)
		{
			preCharacterState = nowState;  //上一个字符的状态 = 现在的状态 ,找下一个状态
			nowState = m_dfa.ConversionState(strline_str[i]); //找到当前字符的状态
			if (nowState.m_isEndState)  //如果当前字符的状态的为蔠态
			{
				preEndState = nowState;   //上一个蔠态出现的位置为当前位置
				isFindEndState = true;
				PreEndState_Character = i;  //上一个被识别到的终结状态的字符位置索引
			}
			if (nowState.m_name == "Wrong")      //如果当前状态为wrong
			{
				if (isFindEndState != false)  //如果曾经成功过,则说明合法,入库
				{
					Push(std::string(strline_str, preCoutStr_EndCharacter + 1, PreEndState_Character - preCoutStr_EndCharacter), preEndState);
					isFindEndState = false;
					preCoutStr_EndCharacter = PreEndState_Character;
					i = PreEndState_Character;
				}
				else  //否则
				{
					if (!m_dfa.isIncludeCharacter(strline_str[i]))  //如果字母表中没有这个单词,则报错没有这个单词
					{
						AddError(std::string(strline_str, preCoutStr_EndCharacter + 1, i - preCoutStr_EndCharacter - 1), strline_str[i], preCharacterState, false);
						preCoutStr_EndCharacter = PreEndState_Character = i;
					}
					else           //如果有,则是未知错误
					{
						AddError(std::string(strline_str, preCoutStr_EndCharacter + 1, i - preCoutStr_EndCharacter - 1), strline_str[i], preCharacterState, true);
						i--;
						preCoutStr_EndCharacter = PreEndState_Character = i;
					}
				}
				preCharacterState = nowState;
				nowState = m_dfa.ReStart();
			}
		}
		strline_rest = std::string(strline_str, preCoutStr_EndCharacter + 1, strline_str.length());
		PreEndState_Character -= (preCoutStr_EndCharacter + 1);
		preCoutStr_EndCharacter = -1;


	}
	AddError(std::string(strline_rest,0, strline_rest.length()), '\0' , preCharacterState, true);
}                 

void CAanlysisManager::ShowError()
{
	for (std::vector<std::string>::const_iterator it = m_errorLog.begin(); it != m_errorLog.end(); it++)
	{
		std::cout << (*it) << std::endl;
	}
}

void CAanlysisManager::Push(const std::string&word, CState state)         //将拆分后的单词按照类别入库
{
	if (state.m_name == "Space_1")return;
	if (state.m_name == "Num_1_9" || state.m_name == "Num_0")
	{
		m_num.push_back(word);
	}
	else if (state.m_name == "Letter")
	{
		m_identifier.push_back(word);
	}
	else if (state.m_name == "Delimiter")
	{
		m_delimiter.push_back(word);
	}
	else if (state.m_name == "Operator_1" || state.m_name == "Operator_3" || state.m_name == "Operator_4")
	{
		m_operator.push_back(word);
	}
	else if (state.m_name == "Annotator_3")
	{
		m_annotations.push_back(word);
	}
	if (m_outfile != nullptr)
	{
		fprintf(m_outfile, "%s\n", word.c_str());
	}
}

按照文件的行读取,当读取到某个字符因为某些原因输出后,我们将此行剩余的东西放入下一行的前端。依次读取

要注意的是下标的转换问题,好好看看。

main程序:

#include "stdafx.h"
#include"AanlysisManager.h"

int _tmain(int argc, _TCHAR* argv[])
{
	CAanlysisManager m_manager;
	std::cout << "输入地址" << std::endl;
	std::string address;
	std::cin >> address;
	m_manager.Open(address.c_str());
	m_manager.RunAnalysis();
	m_manager.ShowError();

	return 0;
}

说白了:

词法分析:

首先构造字母表---》状态集-->状态转换表---》有穷自动机-----》逻辑分析

完。

程序下载地址:http://download.csdn.net/detail/lishuzhai/9492651

运行后输入 .\\ll.txt

即可在lexical.txt中得到词法分析(ll.txt)的结果.

Vs2013

写的有点简单,主要是思路。


你可能感兴趣的:(C++实现词法分析器详解)