这里是lz大二上的编译原理实验课,课设要求模仿编译器的原理思想,用编译语言设计能够识别单词和简单语法的编译,当时摸了两天左右写了小一千行,在这里再回顾一下思路,并留下对应的源码供参考。
实验实现的方法有很多种,其中实验一的词法分析部分比较简单,这里重点分析语法分析部分。
题目需求:
实验一:词法分析
这里用了stl::map等提前存储单词记录出现次数即可。这里就不展开详细描述了。一些具体细节的可参考github中的源码。
map<string, int> sampleDictionary;
void sampleDictionaryInit() //写好对应映射
{
sampleDictionary["and"] = 1;
sampleDictionary["array"] = 2;
...
sampleDictionary["["] = 59;
sampleDictionary["]"] = 60;
return;
}
//写出一些会用到的工具类函数,和二元式的数据结构
struct DoubleGroup {
int a;
int b;
string Word;
DoubleGroup(string w,int x = 0, int y = 0)
{
Word = w;
a = x;
b = y;
}
};
bool isKeyWord(string input) //是 关键字
{
if (sampleDictionary[input])
{
if (sampleDictionary[input] >= 1 && sampleDictionary[input] <= 35)
return true;
}
return false;
}
bool isNum(char Input) //是 数字
{
if (Input >= '0' && Input <= '9')
return true;
else
return false;
}
bool isLetter(char Input)//是 字母
{
return (Input >= 'a' && Input <= 'z') || (Input >= 'A' && Input <= 'Z');
}
bool isDivided(string Input) //是 分隔符
{
if (sampleDictionary[Input])
{
if (sampleDictionary[Input] >= 39 && sampleDictionary[Input] <= 60)
return true;
}
if (Input == " " || Input == "\n")
return true;
return false;
}
实验二:语法分析
这里具体展开讲讲实验二中的实现方法
程序核心思想:LL(1)分析法。让产生式右部逐个文法符号与输入串匹配,每当一个文法符号获得匹配,就可以执行语义动作。
语法分析用到的函数原型:
//语法分析用到的函数原型:
void Gramma_analysis(); //语法分析,负责逐字扫描单词,压语义栈,跳转到对应语义动作
void Gramma_Expression(); //表达式的四元式翻译
void Gramma_Expression(int);
void Gramma_Assignment(); //赋值句的四元式翻译
EState& Gramma_Condition(); //bool式的四元式翻译,返回整个bool式子的状态E
void Gramma_FrontQuaternions(); //四元式生成前调用,在这里匹配语义栈执行对应语义动作
void Gramma_Quaternions_MergeJump(); //在最终输出前先合并一次jump
语法分析用到的数据结构(struct):
其中,一些表达式的属性需要记录,比如bool表达式需要知道入口,一些条件需要对应的真出口与假出口等,并设置根据链表的回填方法。
//四元组
struct Quaternion {
string v1, v2, v3, v4;
int index;
Quaternion(int _index, string _v1 = "-", string _v2 = "-", string _v3 = "-", string _v4 = "-")
{
index = _index;
v1 = _v1;
v2 = _v2;
v3 = _v3;
v4 = _v4;
}
};
vector<Quaternion> Quaternions;
//bool表达式的属性
struct EState
{
int selfpos; //自己在四元组中对应的位置
int _true; //真出口
int _false; //假出口
bool isCorrect; //锁定 真出口&假出口,给e1 or e2 或 e1 and e2情况下使用
EState* _trueList; //回填真出口的链表
EState* _falseList;//回填假出口的链表
EState(int _index) //构造函数
{
selfpos = _index;
_true = -1;
_false = -1;
_trueList = NULL;
_falseList = NULL;
isCorrect = false;
}
EState*& GetTlist() //得到需要回填的真出口链表尾
{
if (_trueList == NULL)
return _trueList;
else
return _trueList->GetTlist();
}
EState*& GetFlist() //得到需要回填的假出口链表尾
{...}
void backpatch_T(int exit) //回填真出口
{
_true = exit;
if (selfpos > 0 && !isCorrect)
{
Quaternions[selfpos].v4 = ToString(_true);
}
if (_trueList == NULL)
return;
else
_trueList->backpatch_T(exit);
}
void backpatch_F(int exit)//回填假出口
{...}
};
//if while repeat的属性
struct if_State
{
EState* E; //对应的bool表达式
int pos_ElseQ; //如果该if出现了对应的else,要记录该E.false的入口和E.true的出口
if_State(EState& _temp)
{
E = &_temp;
pos_ElseQ = -1;
}
void backpatch_T(int exit)
{
E->backpatch_T(exit);
}
void backpatch_F(int exit)
{
E->backpatch_F(exit);
}
};
struct while_State
{
int Enter; //while入口编号
EState* E; //对应的bool表达式
void backpatch_T(int exit)
{
E->backpatch_T(exit);
}
void backpatch_F(int exit)
{
E->backpatch_F(exit);
}
while_State(EState& _temp,int _enter = _curr_index)
{
E = &_temp;
Enter = _enter;
}
};
struct repeat_State
{
int Enter; //repeat入口编号
EState* E; //对应的bool表达式
repeat_State(int _enter)
{
Enter = _enter;
E = NULL;
}
void backpatch_T(int exit)
{
E->backpatch_T(exit);
}
void backpatch_F(int exit)
{
E->backpatch_F(exit);
}
};
存储struct的存储结构(STL)如下
stack<string> StateStack; //语义栈 只会出现if\else\then\while\do的动作符
stack<if_State> ifStack;
stack<while_State> whileStack;
stack<repeat_State> repeatState;
list<EState> e_Storage; //bool表达式、储存ei条件的状态
list<EState> E_stateList; //储存E条件的状态
语法分析Gramma_analysis()函数:(通过Quaternions.push_back()生成四元组)
实验一中已将每个单词存入words数组(string)中,通过扫描words数组的单词,当该单词有对应的语义动作时,调用对应的语义动作:
- 当前单词为:=时,调用赋值句的生成函数Gramma_Assignment();
- 当前单词为if\while\until时:先调用Gramma_Condition()生成对应的bool表达式和四元式,然后生成自身的属性,压入语义栈
- 当前单词为else\repeat\then\do时:压入语义栈
这里写一下对应的伪代码
void sampleAnalyze::Gramma_analysis()
{
while (循环单词表)
{
{...}
if (如果当前单词为:=)
{
Gramma_Assignment();
}
if (如果当前单词为"if")
{
Gramma_FrontQuaternions();
ifStack.push(Gramma_Condition());
StateStack.push("if");
}
if (如果当前单词为"while")
{
Gramma_FrontQuaternions();
int _enter = _curr_index;
whileStack.push( while_State(Gramma_Condition(),_enter) );
StateStack.push("while");
}
if (如果当前单词为"repeat")
{
repeatState.push( repeat_State(-1) ); //标记需要回填repeat的入口
}
if (如果当前单词为"then"/"else"/"do")
{
//压入语义栈
StateStack.push("then")/StateStack.push("else")/StateStack.push("do");
}
if (如果当前单词为"until")
{
repeatState.top().E = &Gramma_Condition();
repeatState.top().backpatch_F(repeatState.top().Enter);//假出口为repeat的入口
repeatState.top().backpatch_T(_curr_index);//真出口为until下一行
repeatState.pop();
};
{...}
}
}
赋值式的处理Gramma_Assignment(): 标记:=位置,调用算式表达式处理Gramma_Expression()后,生成对应四元式。
算式表达式处理Gramma_Expression():扫描整个算式符,标记加减+-;乘除*/的位置,先计算乘除*/,再计算加减+-,生成四元式
void sampleAnalyze::Gramma_Expression()
{
list<int> multip_division; //乘除标志位
list<int> add_sub; //加减标志位
while (遍历算式表达式)
{
if (如果当前单词为"*" || 如果当前单词为"/")
{
记录该单词位置
}
}
Gramma_FrontQuaternions();
//处理乘除,再处理加减
while (遍历标记的所有乘除位置)
{
根据标记位置的前后单词,产生四元式,产生中间代码Ti
}
按照同样逻辑再处理加减运算符
{...}
}
Bool表达式的分析Gramma_Condition(): 先生成一个E表示该个条件式的属性,扫描bool表达式
,每当扫描到>= = <= > < 时生成两个四元组和一个ei,并调用Gramma_Expression()
,e和E的属性使用EState(struct)记录,ei与ej,E之间的关系如下:
- 当E -> e1 时,e1和E同true和false出口
- 当E -> e1 or e2 时,e1.false = e2.pos , e1.true = e2.true = E.true, e2.false = E.false
- 当E -> e1 and e2 时,e1.true = e2.pos , e1.false = e2.false = E.false, e2.true = E.true
不同属性的EState真假出口的通过链表拉链的方式等待回填,函数结束时返回E的地址,留给if\while\repeat属性中的EState*记录
bool IsCondition(string op) //是 bool 符
{
return (op == "<") || (op == "<=") || (op == "=") || (op == ">") || (op == ">=");
}
EState& sampleAnalyze::Gramma_Condition()
{
生成E
while (遍历bool表达式)
{
if (如果读取到了标识符/字符串/数字)
{
Gramma_Expression(_temp_rp);
}
if (op == "<") || (op == "<=") || (op == "=") || (op == ">") || (op == ">=")
{
Gramma_Expression(_temp_rp + 1);
if(如果当前没有标记过的or/and)
{
生成ei
生成四元式*2
链表拉链ei与E的真假出口挂钩
}
else
{
if (存在标记过的or)
{
生成ei
生成四元式*2
链表拉链ei与ei-1和E的真假出口挂钩
弹出该or的位置
}
if (存在标记过的and)
{...}
}
}
if (如果读取到了and)
{
标记and位置
if(orPoint + andPoint > 1)
{
抛出报错:多余的and/or
}
}
{...}
return E的地址
}
检查语义栈,回填E的出入口Gramma_FrontQuaternions():在每次生成四元组前会调用一次,检查语义栈顶的动作词并执行相应动作:
While(语义栈不为空){
- 栈顶为"if":(1)如果if.false未回填,回填if.false,否则(if.false已经被else回填情况下)回填已经标记好的if.true出口,continue
2.栈顶为"else":回填if.false,break
3.栈顶为"do":回填while.true,break
4.栈顶为"then":回填if.true,break
5.栈顶为"while":生成四元组(j,-,-,while.enter),然后再回填while.false,continue
6.栈顶为"repeat":标记repeat入口,continue
}
如出现一些异常情况:如if.false已经回填了栈顶依然出现了‘else’,程序会抛出报错
void sampleAnalyze::Gramma_FrontQuaternions()
{
while (!StateStack.empty())
{
if (StateStack.top() == "if")
{
if (ifStack.top().E->_true != -1 && ifStack.top().E->_false == -1)
//true出口已回填,false出口未回填(无else情况)
{
ifStack.top().backpatch_F(_curr_index); //回填false出口
}
else if (ifStack.top().E->_true == -1) //true出口未回填
{
cout << "语法分析错误:if缺少then";
abort();
}
else if (ifStack.top().E->_true != -1 && ifStack.top().E->_false != -1)
//true出口\false出口已回填(有else情况)
{
Quaternions[ifStack.top().pos_ElseQ].v4 = ToString(_curr_index);
//回填if-exit的出口
}
ifStack.pop();
StateStack.pop();
continue;
}
if (StateStack.top() == "else")
{
if (ifStack.top().E->_true != -1 && ifStack.top().E->_false == -1) //true出口已回填,false出口未回填
{
ifStack.top().pos_ElseQ = _curr_index; //标记if-exit的出口
Quaternions.push_back(Quaternion(_curr_index++,"j"));
ifStack.top().backpatch_F(_curr_index); //回填假出口
}
else if (ifStack.top().E->_true != -1 && ifStack.top().E->_false != -1) //true出口已回填,false出口未回填
{
cout << "语法分析错误:出现了多余的else";
abort();
}
else if (ifStack.top().E->_true == -1) //true出口未回填
{
cout << "语法分析错误:if缺少then";
abort();
}
StateStack.pop(); //弹出then
break;
}
if (StateStack.top() == "then")
{
if (ifStack.top().E->_true != -1) //true出口已回填
{
cout << "语法分析错误:多余的then出现";
abort();
}
if (ifStack.top().E->_true == -1) //true出口未回填
{
ifStack.top().backpatch_T(_curr_index); //回填if.true
}
StateStack.pop();
break;
}
if (StateStack.top() == "while")
{
if (whileStack.top().E->_true == -1) //while.true出口未被回填
{
cout << "语法分析错误:while缺少do";
abort();
}
//产生新四元式(循环回调)
Quaternions.push_back(Quaternion(_curr_index++, "j", "-", "-", ToString(whileStack.top().Enter)));
whileStack.top().backpatch_F(_curr_index);//回填while.false出口
whileStack.pop();
StateStack.pop();
continue;
}
if (StateStack.top() == "do")
{
if (whileStack.top().E->_true != -1) //while.true出口已被回填
{
cout << "语法分析错误:多余的do出现";
abort();
}
//回填while.true出口
whileStack.top().backpatch_T(_curr_index);
StateStack.pop();
break;
}
}
//有需要回填的repeat出口(写在这里的目的是为了先把else的四元式生成完)
if (!repeatState.empty() && repeatState.top().Enter == -1)
{
repeatState.top().Enter = _curr_index;
}
}
在所有语法分析完成后,合并jump,调用PrintQuatrenions()输出所有四元式,程序结束
void PrintQuatrenions()
{
for (int i = 0; i < Quaternions.size(); i++)
{
printf("(%d) (%s,%s,%s,%s)\n", Quaternions[i].index,
Quaternions[i].v1.c_str(),
Quaternions[i].v2.c_str(),
Quaternions[i].v3.c_str(),
Quaternions[i].v4.c_str());
}
}
Github源码
对应的项目源码已贴,欢迎大家参考
GitHub - sugarzo/SampleCompilation: 大二编译原理课设-Sample语言的词法/语法分析器(c++)