词法分析器是干么用的?
词法分析器就是将字符序列转化为单词序列的程序。举个例子来讲:
现在有一行代码:
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 |
|
|
|
|
|
|
|
|
|
得不到其他的状态,则说明当前的分词结束了,说的脑残点:这也就是为什么 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]; } }
状态类:
#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); }
#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
写的有点简单,主要是思路。