前言
前一篇文章写到了有穷自动机的实现。传送门
这次就用之前实现的DFA类来完成一个简单的词法分析器吧。实际上通过学习编译原理,我们可以自己写一个词法生成器的编译程序,具体的写法应该是:正则表达式词法分析器->生成转换系统->通过子集法生成DFA->简化DFA->运行DFA
。但是由于没有时间,并且现在已经有了Lex之类的自动生成器,最近不再写了,毕竟这一套下来还是要很多时间的。
词法分析器
确定语言
本次实验需要设计一个类C语言的语言,现在设计语言如下:
语言种类 | 正则 |
---|---|
标识符 | (字母 |
数字 | (0-9)+ |
算数运算符 | / |
逻辑运算符 | / |
位运算符 | / |
保留字 | / |
需要注意的是本语言设计的标识符只能由字母和下划线组成。
对应的机内码为:
组标识符
组内标识符
确定DFA
我们需要根据上述的正则表达式转换成一个DFA,另外任意的保留字都可以看成一个特殊的标识符,所以我们在设计DFA的时候可以将保留字合并到标识符的识别中去,当识别到一个标识符时,再通过查表运算确认这个标识符是否是一个保留字。下面是根据正则所写出的DFA。
编程
我们把需要实现的词法分析器做成一个叫做Lexical_Analyzer
的类。首先来看一下整个项目的文件依赖:
Lexical_Analyzer的数据成员
接下来先介绍一下类的数据成员。大体上可以分成描述DFA的数据结构以及为了记录扫描指针位置、错误记录、状态转换等等辅助信息的其他数据结构。
描述DFA的成员
其中的描述DFA的成员有:
static vector<byte> Valid_State ;
static vector<byte> Termin_State;
static byte Start_State;
static vector<State_Transfer_Tuple<char>> State_Transfer_Matrix;
map<byte, State_Callback_Fun<char>> States_Fun;
其中的State_Transfer_Matrix
是类似于如下的一个结构体数组,用于描述DFA的状态转移:
vector<State_Transfer_Tuple<char>> Lexical_Analyzer::State_Transfer_Matrix = {
{0,[](char a)->bool {return a == ' ' || a == '\n'; },0}
,
{ 0,[](char Char)->bool {return (Char <= 'Z' && Char >= 'A') || (Char <= 'z' && Char >= 'a') || Char == '_'; },1 }
//....
//....
}
而States_Fun
则是用于DFA位于某些状态时需要处理的一些函数,如回退扫描指针等。
辅助变量
而剩下的结构是辅助词法分析器工作的,其中又包括几个字典和位置记录变量。
static map<byte, Word_Value> DFAState_TypeId_Mapping;
static map<string, byte> Reversed_Word_Mapping;
static map<char, byte> Separtor_Mapping;
char* InputSource = NULL;
unsigned long File_Length;
unsigned long Begin_Pt = 0;
unsigned long End_Pt = 0;
unsigned long Row = 0;
unsigned long Col = 0;
bool IsEnd=false;
前三个Mapping是三个字典,记录了在设计语言时的机内码和单词的映射关系。而剩下的几个则是扫描指针以及源代码文件的相关信息。
Lexical_Analyzer的类函数声明
我们来看一下在为了输出规范的机内码,我们需要在DFA中做什么操作。大致上可以分成两类:
- 返回固定的机内码:除了状态2、5的其他终止状态都是这样的。这个又可以分成两种,需要回滚的不需要回滚扫描指针的。另外对于状态37,我们需要单独设计一个函数来识别机内码;
- 需要生成不固定机内码的:对于状态2、5,在识别出状态之后还需要进行转化,如将字符串转化为数字等这样的操作。
根据上面的抽象,我们可以写出几个状态函数F:
void* State_Function_0(const char&, byte);
void* State_Function_2(const char&, byte);
void* State_Function_5(const char&, byte);
void* State_Function_37(const char&, byte);
void* State_Function_Other(const char&, byte);
其中0状态是用于剔除空白格和换行的;
另外还有入口函数
Word_Value Lex_Analyze();
void* State_Function_Other(const char&, byte);
bool Set_Source_File(const char* File);
对于运行函数Lex_Analyze
,程序运行流程大致如下:
- 检查源文件是否扫描完,源文件是否载入等等情况;
- 剔除空格,换行符等特殊字符;
- 运行DFA类的Run函数,如果运行失败,则抛出异常;
- 运行正常,重置DFA,返回结果;
运行结果
对于这个阶段来说,任意可读字符的输入都是符合条件的,如标识符abc123
将会被识别为{0,"abc"}
和{1,123}
这两个机内码,所以异常抛出暂时无法测试
源码
头文件
#pragma once
#include<string>
#include<vector>
#include<map>
#include"VarType.h"
#include"DFA.hpp"
using namespace std;
using namespace DFA;
typedef struct {
byte Group_Id;
union
{
unsigned long Value;
string* Str;
};
}Word_Value;
namespace LexicalAnalyzer_VarType {
union Symbal_Tuple_Value
{
string* Marker;
int Constant;
};
struct Symbal_Tuple {
byte Type_Id;
Symbal_Tuple_Value Value;
unsigned long Source_Line;
Symbal_Tuple(byte Id, unsigned long Line, void* Pt);
};
}
namespace Lexical_Analyzer_Label {
constexpr byte Marker = 0;
constexpr byte Constant = 1;
constexpr byte Math_Op = 2;
constexpr byte Logic_Op = 3;
constexpr byte Separtor = 4;
constexpr byte Reversed = 5;
constexpr byte Bit_Op = 6;
constexpr byte Error = -1;// )
};
class Lexical_Analyzer {
public:
Lexical_Analyzer();
static vector<byte> Valid_State ;
static vector<byte> Termin_State;
static byte Start_State;
static vector<State_Transfer_Tuple<char>> State_Transfer_Matrix;
map<byte, State_Callback_Fun<char>> States_Fun;
static map<byte, Word_Value> DFAState_TypeId_Mapping;
static map<string, byte> Reversed_Word_Mapping;
static map<char, byte> Separtor_Mapping;
char* InputSource = NULL;
unsigned long File_Length;
unsigned long Begin_Pt = 0;
unsigned long End_Pt = 0;
unsigned long Row = 0;
unsigned long Col = 0;
bool IsEnd=false;
Dfa<char> Lex_DFA;
//*******回滚和同步指针********//
void RollbackEndPt();
void SynBeginEndPt();
//*******回滚和同步指针********//
//*******状态函数F********//
void* State_Function_0(const char&, byte);
void* State_Function_2(const char&, byte);
void* State_Function_5(const char&, byte);
void* State_Function_37(const char&, byte);
void* State_Function_Other(const char&, byte);
//*******状态函数F********//
Word_Value Lex_Analyze();
bool Set_Source_File(const char* File);
};
cpp
#include "Lexical_Analyzer.h"
#include <string>
#include<stdlib.h>
#include"DFA.hpp"
#include<cstring>
#include<stdlib.h>
#pragma warning(disable:4996)
vector<byte> Lexical_Analyzer::Valid_State = { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,38,39 };
vector<byte> Lexical_Analyzer::Termin_State = { 2, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19,
20, 22, 38,23,25,39,26,28,29,31,32,34,35,36,37,38,39 };
vector<State_Transfer_Tuple<char>> Lexical_Analyzer::State_Transfer_Matrix = {
{0,[](char a)->bool {return a == ' ' || a == '\n'; },0}
,
{ 0,[](char Char)->bool {return (Char <= 'Z' && Char >= 'A') || (Char <= 'z' && Char >= 'a') || Char == '_'; },1 }
,
{ 1,[](char Char)->bool {return (Char <= 'Z' && Char >= 'A') || (Char <= 'z' && Char >= 'a') || Char == '_'; },1 }
,
{ 1,[](char Char)->bool {return !((Char <= 'Z' && Char >= 'A') || (Char <= 'z' && Char >= 'a') || Char == '_'); },2 }
,
{ 0,[](char Char)->bool {return Char >= '0' && Char <= '9'; },4 }
,
{ 4,[](char Char)->bool {return Char >= '0' && Char <= '9'; },4 }
,
{ 4,[](char Char)->bool {return !(Char >= '0' && Char <= '9'); },5 }
,
{ 0,[](char Char)->bool {return Char == '+'; },6 }
,
{ 6,[](char Char)->bool {return Char == '+'; },8 }
,
{ 6,[](char Char)->bool {return Char != '+'; },7 }
,
{ 0,[](char Char)->bool {return Char == '-'; },9 }
,
{ 9,[](char Char)->bool {return Char == '-'; },11 }
,
{ 9,[](char Char)->bool {return Char != '-'; },10 }
,
{ 0,[](char Char)->bool {return Char == '*'; },12 }
,
{ 12,[](char Char)->bool {return Char != '*'; },13 }
,
{ 12,[](char Char)->bool {return Char == '*'; },14 }
,
{ 0,[](char Char)->bool {return Char == '/'; },15 }
,
{ 15,[](char Char)->bool {return Char == '/'; },17 }
,
{ 15,[](char Char)->bool {return Char != '/'; },16 }
,
{ 0,[](char Char)->bool {return Char == '!'; },18 }
,
{ 18,[](char Char)->bool {return Char != '!'; },19 }
,
{ 18,[](char Char)->bool {return Char == '='; },20 }
,
{ 0,[](char Char)->bool {return Char == '<'; },21 }
,
{ 21,[](char Char)->bool {return Char != '/' && Char != '<'; },22 }
,
{ 21,[](char Char)->bool {return Char == '<'; },38 }
,
{ 21,[](char Char)->bool {return Char == '='; },23 }
,
{ 0,[](char Char)->bool {return Char == '>'; },24 }
,
{ 24,[](char Char)->bool {return Char != '>' && Char != '='; },25 }
,
{ 24,[](char Char)->bool {return Char == '>'; },39 }
,
{ 24,[](char Char)->bool {return Char == '='; },26 }
,
{ 0,[](char Char)->bool {return Char == '='; },27 }
,
{ 27,[](char Char)->bool {return Char != '='; },28 }
,
{ 27,[](char Char)->bool {return Char == '='; },29 }
,
{ 0,[](char Char)->bool {return Char == '&'; },30 }
,
{ 30,[](char Char)->bool {return Char != '&'; },31 }
,
{ 30,[](char Char)->bool {return Char == '&'; },32 }
,
{ 0,[](char Char)->bool {return Char == '|'; },33 }
,
{ 33,[](char Char)->bool {return Char != '|'; },34 }
,
{ 33,[](char Char)->bool {return Char == '|'; },35 }
,
{ 0,[](char Char)->bool {return Char == '~'; },36 }
,
{ 0,
[](char Char)->bool {
constexpr char Contrast_Str[] = "()[]{};,";
for (int i = 0; i < sizeof(Contrast_Str); i++)
{
if (Char == Contrast_Str[i])
return true;
}
return false;
}
,37
}
};
byte Lexical_Analyzer::Start_State = 0;
typedef pair<byte, Word_Value> Pair_Word_Value;
map<byte, Word_Value> Lexical_Analyzer::DFAState_TypeId_Mapping = {
Pair_Word_Value(2,{0,0}),
Pair_Word_Value(5,{1,0}),
Pair_Word_Value(7,{2,0}),
Pair_Word_Value(8,{2,7}),
Pair_Word_Value(10,{2,1}),
Pair_Word_Value(11,{2,6}),
Pair_Word_Value(13,{2,2}),
Pair_Word_Value(14,{2,4}),
Pair_Word_Value(16,{2,3}),
Pair_Word_Value(17,{2,5}),
Pair_Word_Value(19,{3,0}),
Pair_Word_Value(20,{3,3}),
Pair_Word_Value(22,{3,1}),
Pair_Word_Value(23,{3,4}),
Pair_Word_Value(25,{3,2}),
Pair_Word_Value(26,{3,5}),
Pair_Word_Value(28,{4,6}),
Pair_Word_Value(29,{3,8}),
Pair_Word_Value(31,{6,1}),
Pair_Word_Value(32,{3,6}),
Pair_Word_Value(34,{6,2}),
Pair_Word_Value(35,{3,7}),
Pair_Word_Value(36,{6,0}),
Pair_Word_Value(38,{6,3}),
Pair_Word_Value(39,{6,4}),
};
typedef pair<string, byte> Pair_Type_Id;
map<string, byte> Lexical_Analyzer::Reversed_Word_Mapping = { \
Pair_Type_Id(string("if"),0),
Pair_Type_Id(string("while"), 1),
Pair_Type_Id(string("do"), 2),
Pair_Type_Id(string("for"), 3),
Pair_Type_Id(string("int"), 4),
Pair_Type_Id(string("float"), 5),
Pair_Type_Id(string("char"), 6),
Pair_Type_Id(string("unsigned"), 7),
Pair_Type_Id(string("double"), 8),
};
typedef pair<char, byte> Pair_Sepator;
map<char, byte> Lexical_Analyzer::Separtor_Mapping = { \
Pair_Sepator('(',0),
Pair_Sepator(')', 1),
Pair_Sepator('[', 2),
Pair_Sepator(']', 3),
Pair_Sepator('{', 4),
Pair_Sepator('}', 5),
Pair_Sepator(';', 7),
Pair_Sepator(',', 8),
};
void Lexical_Analyzer::RollbackEndPt() {
End_Pt--;
}
void Lexical_Analyzer::SynBeginEndPt() {
Begin_Pt = End_Pt;
}
Lexical_Analyzer::Lexical_Analyzer() {
State_Callback_Fun<char> F = std::bind(&Lexical_Analyzer::State_Function_0, this, placeholders::_1, placeholders::_2);
States_Fun[0] = F;
F = std::bind(&Lexical_Analyzer::State_Function_2, this, placeholders::_1,placeholders::_2);
States_Fun[2] = F;
F= std::bind(&Lexical_Analyzer::State_Function_5, this, placeholders::_1, placeholders::_2);
States_Fun[5] = F;
F = std::bind(&Lexical_Analyzer::State_Function_37, this, placeholders::_1, placeholders::_2);
States_Fun[37] = F;
F = std::bind(&Lexical_Analyzer::State_Function_Other, this, placeholders::_1, placeholders::_2);
for (byte i:Termin_State)
{
if (i != 2 && i != 5 && i!=37) {
States_Fun[i] = F;
}
}
Lex_DFA.Set_Start_State(Start_State);
Lex_DFA.Set_Termin_State(Termin_State);
Lex_DFA.Set_Transfer_Matrix(State_Transfer_Matrix);
Lex_DFA.Set_Termin_State(Termin_State);
Lex_DFA.Set_Valid_States(Valid_State);
Lex_DFA.Set_State_Fun(States_Fun);
Lex_DFA.Set_Input_Check([](char&) ->bool {return true; });
}
Word_Value Lexical_Analyzer::Lex_Analyze() {
if (!InputSource)
throw exception("No Input File!\n");
Word_Value* Result=NULL;
if (End_Pt == File_Length) {
IsEnd = true;
Word_Value Placeholder = { 0,0 };
return Placeholder;
}
while (InputSource[End_Pt])
{
char Current_Char = InputSource[End_Pt++];
Col++;
if (Current_Char == '\n') {
Row++;
Col = 0;
}
if (Current_Char == ' ') {
}
if (!Lex_DFA.Run(Current_Char, (void**)&Result)) {
string Exception_Info = "Syntax Wrong at Line " + to_string(Row) + "\n";
throw Exception_Info;
}
if (Lex_DFA.Is_Termination_State) {
Lex_DFA.Restart();
return *Result;
}
}
}
void* Lexical_Analyzer::State_Function_0(const char&, byte) {
Begin_Pt++;
return NULL;
}
void* Lexical_Analyzer::State_Function_2(const char& Input,byte Current_State) {
Word_Value* Result = new Word_Value;
this->RollbackEndPt();
string* TmpStr = new string;
for (int i = this->Begin_Pt; i < this->End_Pt; i++)
{
((string*)TmpStr)->push_back(*(this->InputSource + i));
}
map<string,byte>::iterator It=Reversed_Word_Mapping.find(*TmpStr);
if (It != Reversed_Word_Mapping.end()) {
Result->Group_Id = 5;
Result->Value = It->second;
}
else {
*Result = DFAState_TypeId_Mapping.find(Current_State)->second;
Result->Str = TmpStr;
}
this->SynBeginEndPt();
return Result;
}
void* Lexical_Analyzer::State_Function_5(const char& Input, byte Current_State) {
Word_Value* Result = new Word_Value;
this->RollbackEndPt();
string* TmpStr = new string;
for (int i = this->Begin_Pt; i < this->End_Pt; i++)
{
((string*)TmpStr)->push_back(*(this->InputSource + i));
}
*Result = DFAState_TypeId_Mapping.find(Current_State)->second;
Result->Value = strtoul(TmpStr->c_str(), NULL, 10);
delete TmpStr;
this->SynBeginEndPt();
return Result;
}
void* Lexical_Analyzer::State_Function_37(const char& Input, byte Current_State) {
map<char, byte>::iterator It = Separtor_Mapping.find(Input);
if (It == Separtor_Mapping.end())
throw new exception();
Word_Value* Result = new Word_Value;
Result->Group_Id = 4;
Result->Value = It->second;
this->SynBeginEndPt();
return Result;
}
void* Lexical_Analyzer::State_Function_Other(const char& Input, byte Current_State) {
Word_Value* Result = new Word_Value;
switch (Current_State)
{
case 7:
case 10:
case 13:
case 16:
case 19:
case 22:
case 25:
case 28:
case 31:
case 34:
this->RollbackEndPt();
break;
default:
break;
}
*Result = DFAState_TypeId_Mapping.find(Current_State)->second;
this->SynBeginEndPt();
return Result;
}
bool Lexical_Analyzer::Set_Source_File(const char* File_Path) {
FILE* fp = fopen(File_Path, "r");
if (!fp)
return false;
fseek(fp, 0, 0);
fseek(fp, 0, SEEK_END);
File_Length = ftell(fp);
fseek(fp, 0, 0);
InputSource = new char[File_Length + 1];
fread((void*)InputSource, 1, File_Length, fp);
InputSource[File_Length] = '\0';
fclose(fp);
return true;
}