东莞网站建设代理wordpress文章怎么生成标签
东莞网站建设代理,wordpress文章怎么生成标签,厦门网站建设建设公司,网络工程师可以从事什么工作编译原理课程实践——实现具有初等函数运算语言的解释器或编译器
作者#xff1a;Sylvan Ding #xff5c;转载请注明文章出处#xff01; 摘要#xff1a;本文主要内容是设计词法分析器、语法分析器#xff08;LL(1)、SLR(1)#xff09;和语义分析器#xff08;基于SL…编译原理课程实践——实现具有初等函数运算语言的解释器或编译器
作者Sylvan Ding 转载请注明文章出处 摘要本文主要内容是设计词法分析器、语法分析器LL(1)、SLR(1)和语义分析器基于SLR(1)实现一个初等函数运算语言的解释器或编译器。在设计词法分析器和语法分析器时由于考虑尚不完善以至于进行语义分析和语法制导翻译时存在困难但本文仍能让读者了解构建词法分析器和递归下降的语法分析器的原理和基本方法掌握最简单的编译器的实现过程。 --【目录】--
第1章 前言 1
第2章 词法分析器设计 2
2.1 实验目的 2
2.2 实验内容及要求 2
2.3 实验过程 2
2.3.1 设计实数的正规式并构造NFA 2
2.3.2 设计标识符的正规式并构造NFA 3
2.3.3 设计运算符和界符的正规式并构造NFA 4
2.3.4 合并NFA 4
2.3.5 NFA确定化为DFA 4
2.3.6 分划法简化DFA 8
2.3.7 编写词法分析程序 10
2.3.8 结果分析 18
第3章 语法分析器设计 20
3.1 实验目的 20
3.2 实验内容及要求 20
3.3 实验过程 20
3.3.1 算术表达式文法的描述 20
3.3.2 算术表达式文法消除回溯 21
3.3.3 赋值表达式的文法描述 23
3.3.4 语言程序的文法描述 23
3.3.5 语言文法的描述 24
3.3.6 文法G[P]的无回溯性检验 24
3.3.7 预测分析表的构造 27
3.3.8 递归下降分析器的设计 28
3.3.9 递归下降分析程序中错误处理方式的优化 29
3.3.10 结果分析 31
3.3.11 代码实现 37
第4章 语义分析 48
4.1 实验目的 48
4.2 实验内容及要求 48
4.3 实验过程 48
4.3.1 引言 48
4.3.2 LR分析器的工作原理和过程 49
4.3.3 LR(0)分析法 50
4.3.4 语法制导翻译器的设计与实现 60
4.3.5 符号表的组织与管理 63
4.3.6 目标代码生成器的设计与实现 63
4.3.7 结果分析 64
4.3.8 代码实现 64
第5章 参考文献 91第1章 前言
本实验主要内容是实现一个初等函数运算语言的解释器或编译器。初等函数是由幂函数、指数函数、对数函数、三角函数、反三角函数与常数经过有限次的有理运算加、减、乘、除及有限次函数复合所产生、并且能用一个解析式表示的函数。
如下表所示本实验中仅要求完成部分初等函数包括三角函数、幂函数、指数函数、对数等类型。 本程序从输入界面或文件中接收一个包含了各种初等函数表达式的字符串程序对这些表达式进行计算和求值并根据要求输出相应的值。 初等函数运算语言相关的内容如下
1语言中仅使用实数这一种数据类型。所有常数、变量及表达式都为实数类型。
2语言中可以定义变量来存放数值变量的定义方式与c语言中的标识符相同。
3可以通过赋值语句给变量赋值。
4表达式是一个初等函数函数、变量、常数等通过四则运算或函数嵌套而成。
5输出语句是?表达式。将在界面上输出该表达式的值。如果其中有某一个变量没有赋值那么将输出该表达式简化后的式子。
第2章 词法分析器设计
2.1 实验目的
1、为初等函数运算语言构造词法分析器。
2、掌握生成词法分析器的方法加深对词法分析原理的理解。
3、掌握设计、编制并调试词法分析程序的思想和方法。
2.2 实验内容及要求
根据下面的要求设计初等函数运算语言的词法模式并用正则式表达出来。
初等函数运算语言的常量为实数类型其定义方式为实数的最一般书写方式如123.321。具体要求不支持整数部分大于0时首数字为0不支持小数点后结尾为0不支持科学记数法不支持仅为整数时有小数点。初等函数运算语言的变量采用与C语言的标识符定义一样的方式首字符为字母或下划线其他的为字母、数字及下划线的混合串区分大小写变量长度不超过32个字符。初等函数运算语言需要处理的函数仅为表一中所列举的内容。初等函数运算语言支持四则运算其计算的符号与C语言相同为±*/。初等函数运算语言的合法的分隔符包括空格、制表符、分行符圆括号左、右、分号。其中空格、制表符、分行符可以出现在任何两个不同的单词中间圆括号左、右用于表达式中用于改变运算的优先级以及标识函数的参数分号用于标识一个语句的结束。初等函数运算语言支持的常量还包括PIE。
2.3 实验过程
2.3.1 设计实数的正规式并构造NFA
设计满足上述要求1的实数的正规式
R1(((1∣…∣9)(0∣…∣9)∗)∣0)((.(0∣…∣9)∗(1∣…∣9))∣ϵ)R1\ \ (((1|\ldots|9)(0|\ldots|9)\ast)|0)((.(0|\ldots|9)\ast(1|\ldots|9))|\epsilon)R1 (((1∣…∣9)(0∣…∣9)∗)∣0)((.(0∣…∣9)∗(1∣…∣9))∣ϵ)
其中
(1∣…∣9)(0∣…∣9)∗(1|\ldots|9)(0|\ldots|9)\ast(1∣…∣9)(0∣…∣9)∗ 满足“不支持整数部分大于0时首数字为0”
(.(0∣…∣9)∗(1∣…∣9))(.(0|\ldots|9)\ast(1|\ldots|9))(.(0∣…∣9)∗(1∣…∣9)) 满足“不支持小数点后结尾为0”
((.(0∣…∣9)∗(1∣…∣9))∣ϵ)((.(0|\ldots|9)\ast(1|\ldots|9))|\epsilon)((.(0∣…∣9)∗(1∣…∣9))∣ϵ) 满足“不支持仅为整数时有小数点”
(((1∣…∣9)(0∣…∣9)∗)∣0)(((1|\ldots|9)(0|\ldots|9)\ast)|0)(((1∣…∣9)(0∣…∣9)∗)∣0) 考虑了整数部分为0时的情况。
具体定义过程是
zero0zero\ \ 0zero 0
nonzero1∣...∣9nonzero\ \ 1\ |\ ...\ |\ 9nonzero 1 ∣ ... ∣ 9
digitzero∣nonzerodigit\ \ zero\ |\ nonzerodigit zero ∣ nonzero
digitsdigitdigit∗digits\ \ digit\ digit\ \astdigits digit digit ∗
integerPart(nonzerodigit∗)∣zerointegerPart\ \ (nonzero\ digit\ \ast)\ |\ zerointegerPart (nonzero digit ∗) ∣ zero
optionalFraction(.digit∗nonzero)∣ϵoptionalFraction\ \ (.\ digit\ \ast\ nonzero)\ |\ \epsilonoptionalFraction (. digit ∗ nonzero) ∣ ϵ
NumberintegerPartoptionalFractionNumber\ \ integerPart\ optionalFractionNumber integerPart optionalFraction 2.3.2 设计标识符的正规式并构造NFA
运算语言支持的函数需求3、常量PI和E需求6可作为一类特殊的标识符来处理不再专设对应的转换图用户不得使用它们作为自己定义的标识符但需要把它们预先安排在一个表格中称为关键字表。当利用DFA识别出一个“标识符”时就去查关键字表以确定标识符是否为一常量或函数。
设计满足上述要求2的标识符的正规式
digit0∣...∣9digit\ \ 0|...|9digit 0∣...∣9
lettera∣...∣z∣A∣...∣Z∣_letter\ \ a|...|z|A|...|Z|\_letter a∣...∣z∣A∣...∣Z∣_
idletter(letter∣digit)∗id\ \ letter(letter|digit)\astid letter(letter∣digit)∗ 2.3.3 设计运算符和界符的正规式并构造NFA
运算符 - * / ^ ?
界符( ) ;
以运算符?为例构造其正规式和NFA其余符号的构造方法类似。
R3?R3\ \ ?R3 ? 2.3.4 合并NFA
将上述正则表达式生成的NFA合并成一个新的NFA 2.3.5 NFA确定化为DFA
使用子集法将NFA N确定化为DFA M。从NFA构造DFA的基本思想是DFA的每一个状态代表NFA状态集合的某个子集该DFA使用它的状态去记录在NFA读入输入符号之后可能到达的所有状态的集合。
从 NFAN(Q,Σ,f,S,Z)NFA\ N\left(Q,\Sigma,f,S,Z\right)NFA N(Q,Σ,f,S,Z) 构造等价的 DFA M(Q′,Σ′,f′,S′,Z′)M\left(Q^\prime,\Sigma^\prime,f^\prime,S^\prime,Z^\prime\right)M(Q′,Σ′,f′,S′,Z′) 的基本方法是
首先将从状态SSS出发经过任意条 ϵ\epsilonϵ 弧所能到达的状态所组成的集合作为M的初态S′SS′然后从S′SS′出发经过对输入符号 a∈Σa\in\Sigmaa∈Σ 的状态转移所能到达的状态的 ϵ−CLOSURE\epsilon-CLOSUREϵ−CLOSURE 所组成的集合作为M的新状态如此重复直到不再有新的状态出现为止。构造Q′QQ′和f′ff′的算法描述如下 使用上述子集法求NFA N的等价DFA M
注q0,q1,…,qn∈Q′q0,q1,\ldots,qn\in Q^\primeq0,q1,…,qn∈Q′简记为0′,1′,…,n′0^\prime,1^\prime,\ldots,n^\prime0′,1′,…,n′N、M中终态用下划线标记.
0 S {0,1,9,13,15,17,19,21,23,25,27,29}
1 f(0,nonzero) {2,3,4,8}
2 f(0,zero) {4,8}
3 f(0,letter) {10,11,12}
4 f(0,) {14}
5 f(0,-) {16}
6 f(0,*) {18}
7 f(0,/) {20}
8 f(0,^) {22}
9 f(0,() {24}
10 f(0,)) {26}
11 f(0,;) {28}
12 f(0,?) {30}
13 f(1,digit) {3,4,8}
14 f(1,.) {5,6,7}
14 f(2,.) {5,6,7}
15 f(3,letter|digit) {11,12}
13 f(13,digit) {3,4,8}
14 f(13,.) {5,6,7}
16 f(14,zero) {6,7}
17 f(14,nonzero) {6,7,8}
15 f(15,letter|digit) {11,12}
16 f(16,zero) {6,7}
17 f(16,nonzero) {6,7,8}
16 f(17,zero) {6,7}
17 f(17,nonzero) {6,7,8}对上述状态重新编号得到等价DFA如下 2.3.6 分划法简化DFA
通过分组合并等价状态使得DFA没有多余状态且没有两个状态互相等价。DFA M最小化方法是把M的状态集Q’分划成一些不相交的子集使得每个子集中任何两个状态是等价的而任何两个属于不同子集的状态都是可区别的然后在每个子集中任取一个状态作代表而删去子集中其余状态并把射向其余状态的弧都改为射向作为代表的状态。 使用上述算法对DFA M进行化简。M中没有多余状态只有{0,1,2,13,14,16,17},{0,3,15}两组状态需要化简这两组状态转移分别用来识别数字和标识符或函数关键字。对于后者化简的结果为 Π0{{3,15},{0}}\Pi_0\{\{3,15\},\{0\}\}Π0{{3,15},{0}} 。故现在只需要化简前者 由分划法结果可知等价状态为{1,13}, {14,16}, {3,15}. 在每个等价类中选取一个状态作为代表构造简化后的DFA M’对化简后的状态进行了重新编码 用五元组 M′(Q,Σ,f,S,Z)M^\prime\left(Q,\Sigma,f,S,Z\right)M′(Q,Σ,f,S,Z) 描述上述最小化DFA Q{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}Q \{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15\}Q{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} Σ{digit,letter,.,,−,∗,/,,(,),;,?,}\Sigma \{digit, letter, ., , -, *, /, ^, (, ), ;, ?, \}Σ{digit,letter,.,,−,∗,/,,(,),;,?,} S0S 0S0 Z{1,2,3,4,5,6,7,8,9,10,11,12,13,15}Z \{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15\}Z{1,2,3,4,5,6,7,8,9,10,11,12,13,15}
其中终态1、2、15用来识别数字终态3用来识别标识符包括常量、函数名终态413用来识别符号包括运算符、界符。更具体地状态1识别非零整数状态2识别整数零状态15识别浮点数。 2.3.7 编写词法分析程序
根据实验要求列写出该语言的所有单词符号 规定若函数名、标识符和常数之间没有确定的运算符或界符作间隔则必须至少用一个空白符作间隔即此时的空白符是有意义的。接下来根据状态转换图构造出词法分析器在此引进词法分析程序所用的全局变量和需要调用的函数如下
ch字符变量存放当前读进的源程序字符token字符数组存放构成单词符号的字符串MAX_TOKEN_LEN整型常量存放最大token长度按题意设置为32TokenCode枚举类型列出所有符号及其种别码getch()读字符函数每调用一次从输入缓冲区中读进源程序的下一个字符放在ch中并把读字符指针指向下一个字符getbc()函数每次调用时检查ch中的字符是否为空白字符包括空格、换行符和制表符若是空白字符则反复调用getch()直至ch中读入一个非空白字符为止concat()函数每次调用把当前ch中的字符与token中的字符串连接同时检查token是否超过MAX_TOKEN_LEN若超过则报错letter(ch)、digit(ch)、zero(ch)、nonzero(ch)布尔函数分别判定ch中的字符是否为字母和数字以及数字的类型从而给出true或false值reserve()整型函数对token中的字符串查关键字表若它是一个关键字函数名或常量则返回它的编码否则返回标识符的种别码11。关键字表用generateKeyList()来生成关键字表存放在keyList中retract()函数读字符指针回退一个字符return()函数收集并携带必要的信息返回调用程序即返回语法分析程序stof()函数将token中的数字串转换成浮点数词法分析程序scan()返回的单词符号为二元式单词种别单词自身值在程序中以Word类表征。仅当单词种别是标识符和数字时单词自身值才存在并以字符串形式保存saveWord(Word)将scan()返回的结果保存在vector容器中。
//
// Created by Sylvan Ding on 2022/5/10.
//#include iostream
#include cstring
#include vector
#include mapusing namespace std;// 最大TOKEN长度
const int MAX_TOKEN_LEN 32;// 定义单词编码
enum TokenCode {TC_UNDEF, // 0TC_SIN, // sin 1TC_COS, // cos 2TC_TG, // tg 3TC_CTG, // ctg 4TC_LOG, // log 5TC_LG, // lg 6TC_LN, // ln 7TC_PI, // PI 8TC_E, // E 9TC_ID, // ID 10TC_NUM, // NUM 11TC_ADD, // 12TC_SUB, // - 13TC_TIM, // * 14TC_DIV, // / 15TC_POW, // ^ 16TC_EQU, // 17TC_RUN, // ? 18TC_LBR, // ( 19TC_RBR, // ) 20TC_SEP, // ; 21
};class Word {
public:void print() const;Word(TokenCode tc, const string tv ) : tcode(tc), tval(tv) {}private:TokenCode tcode; // 单词种别string tval; // 单词值
};void Word::print() const {printf(%d,%s\n, tcode, tval.c_str());
}class LexicalAnalyze {
public:explicit LexicalAnalyze(const string pg);void printSucInfo() const;void printWords() const;vectorWord words;string program;mapstring, TokenCode keyList;private:char ch{};string token{};int token_len{};string::iterator sit{};Word scaner();void getch();void getbc();void concat();void retract();bool letter(char c);bool zero(char c);bool nonzero(char c);bool digit(char c);TokenCode reserve();void generateKeyList();void saveWord(const Word w);void tokenClear();void error();
};LexicalAnalyze::LexicalAnalyze(const string pg) : program(pg), token_len(0) {generateKeyList();sit program.begin();while (sit program.end()) {getch();getbc();if (ch \0)break;saveWord(scaner());}printSucInfo();
}Word LexicalAnalyze::scaner() {tokenClear();if (nonzero(ch)) {while (digit(ch)) {concat();getch();}if (ch .) {concat();getch();while (true) {while (zero(ch)) {concat();getch();}if (nonzero(ch)) {while (nonzero(ch)) {concat();getch();}if (zero(ch))continue;else {retract();return Word(TC_NUM, token);}} else {error();}}} else {retract();return Word(TC_NUM, token);}} else if (zero(ch)) {concat();getch();if (ch .) {concat();getch();while (true) {while (zero(ch)) {concat();getch();}if (nonzero(ch)) {while (nonzero(ch)) {concat();getch();}if (zero(ch))continue;else {retract();return Word(TC_NUM, token);}} else {error();}}} else {retract();return Word(TC_NUM, token);}} else if (letter(ch)) {while (letter(ch) || digit(ch)) {concat();getch();}retract();TokenCode rs reserve();return rs TC_ID ? Word(TC_ID, token) : rs;} else {switch (ch) {case :return TC_ADD;case -:return TC_SUB;case *:return TC_TIM;case /:return TC_DIV;case ^:return TC_POW;case (:return TC_LBR;case ):return TC_RBR;case ;:return TC_SEP;case ?:return TC_RUN;case :return TC_EQU;default:error();}}return TC_UNDEF;
}void LexicalAnalyze::getch() {ch *sit;sit;
}void LexicalAnalyze::getbc() {while (ch || ch \t || ch \n)getch();
}void LexicalAnalyze::concat() {if (token_len MAX_TOKEN_LEN)error();token token ch;token_len;
}bool LexicalAnalyze::letter(char c) {return (c A c Z) || (c a c z) || c _;
}bool LexicalAnalyze::zero(char c) {return c 0;
}bool LexicalAnalyze::nonzero(char c) {return c 1 c 9;
}bool LexicalAnalyze::digit(char c) {return zero(c) || nonzero(c);
}void LexicalAnalyze::retract() {sit--;
}TokenCode LexicalAnalyze::reserve() {auto kwi keyList.find(token);TokenCode tc TC_ID;if (kwi ! keyList.end())tc kwi-second;return tc;
}// 定义关键字表
void LexicalAnalyze::generateKeyList() {keyList.insert(pairstring, TokenCode(sin, TC_SIN));keyList.insert(pairstring, TokenCode(cos, TC_COS));keyList.insert(pairstring, TokenCode(tg, TC_TG));keyList.insert(pairstring, TokenCode(ctg, TC_CTG));keyList.insert(pairstring, TokenCode(log, TC_LOG));keyList.insert(pairstring, TokenCode(lg, TC_LG));keyList.insert(pairstring, TokenCode(ln, TC_LN));keyList.insert(pairstring, TokenCode(PI, TC_PI));keyList.insert(pairstring, TokenCode(E, TC_E));
}void LexicalAnalyze::error() {cout \033[31mLexical analysis failed!\033[0m endl;exit(0);
}void LexicalAnalyze::saveWord(const Word w) {words.push_back(w);
}void LexicalAnalyze::printSucInfo() const {cout \033[32mLexical analysis succeeded!\033[0m endl;cout Your program is: \n program endl;printWords();
}void LexicalAnalyze::printWords() const {cout Result of lexical analysis is: endl;for (const auto word : words)word.print();
}void LexicalAnalyze::tokenClear() {token.clear();token_len 0;
}int main() {string program ?1/3*(ln(y)5*sin(x))(7z)^2;;LexicalAnalyze lexAna(program);return 0;
}
2.3.8 结果分析 第3章 语法分析器设计
3.1 实验目的
1、为初等函数运算语言构造LL1语法分析器 2、掌握LL1语法分析器的方法加深对自上而下语法分析原理的理解 3、掌握设计、编制并调试LL1语法分析程序的思想和方法。
3.2 实验内容及要求
1、根据初等函数运算语言运算法则将语法模式用上下文无关文法表达
2、注意运算的优先性避免产生二义性文法将上述文法改写为LL(1)文法
3、根据LL1文法给出预测分析表
4、根据预测分析表给出解析LL(1)文法的递归下降子程序
5、本语法分析程序的输入是实验一生成的记号流本程序需定义语法树的数据结构语法分 析的输出是一棵语法树
6、当输入存在语法错误时给出语法错误提示指出语法错误发生位置和错误类型。
3.3 实验过程
3.3.1 算术表达式文法的描述
【算术表达式的文法描述】 【算术表达式文法的二义性消除】 为上述文法符号重新命名这样就得到了带优先级和结合性的算术表达式文法 终结符小写、非终结符大写G1 3.3.2 算术表达式文法消除回溯
在自上而下分析过程中由于回溯需要推翻前面的分析包括已经做的语义工作重新进行试探大大降低了语法分析器的工作效率因此需要消除回溯。由LL(1)文法的定义可知若文法中含有左递归或含有公共左因子则该文法不是LL(1)文法。因此对于某些非LL(1)文法可以通过消除左递归和反复提取公共左因子对文法进行等价变换将其改造为LL(1)文法。
【算术表达式消除左递归】
左递归会导致自上而下的语法分析过程陷入无限循环。因为若存在形如 A→AαA\rightarrow A\alphaA→Aα 的左线性产生式或是存在推导分析过程又使用最左推导在递归的过程中就会陷入无限循环。对含有直接左递归的文法规则进行等价变换以消除左递归方法如下 【算术表达式提取公共左因子】 3.3.3 赋值表达式的文法描述
相较于算术表达式赋值表达式的优先级更低其含义是把赋值号右边的表达式赋值给左边的一个标识符赋值表达式的文法如下
assign→idexprassign\rightarrowidexprassign→idexpr
为其中非终结符重新命名得到如下文法G3
D→idED\rightarrow idED→idE
3.3.4 语言程序的文法描述
根据“实验要求的输入输出示例表”“程序”用来定义一个合法的语言结构由赋值语句和运算语句组成赋值语句包含多条赋值表达式而运算语句则只能包含一条运算语句其文法定义如下G4
program→assignmentcalculateprogram\rightarrowassignmentcalculateprogram→assignmentcalculate
assignment→D;assignment∣ϵassignment\rightarrow D;assignment|\epsilonassignment→D;assignment∣ϵ
calculate→?E;calculate\rightarrow?E;calculate→?E;
程序语言分区思想的示意图如下 对文法G4进行重新命名并化简后得到文法G5
P→S?E;P\rightarrow S?E;P→S?E;
S→D;S∣ϵS\rightarrow D;S|\epsilonS→D;S∣ϵ
3.3.5 语言文法的描述
结合文法G2、G3、G5我们得到了描述该语言的文法G[P] 3.3.6 文法G[P]的无回溯性检验
综上所述在自上而下分析过程中为了避免回溯对描述语言的文法有一定的要求即要求描述语言的文法是LL(1)文法。为了建立LL(1)文法的判断条件引入First集、Follow集、Select集其定义如下 特别的对于文法G计算其First集和Follow集的算法如下 使用上述算法计算G[P]的First集、Follow集和Select集首先改写文法G[P]使每个候选式单独一行并进行编号G’[P] 【候选式的First集】 【非终结符的First集】 【非终结符的Follow集】 【候选式的Select集】 上述Select集说明同左部的候选式对下一个输入不会产生回溯即文法G[P]是LL(1)文法可以使用预测分析法进行自上而下的语法分析。
3.3.7 预测分析表的构造
预测分析表是一个M[A,a]M[A,a]M[A,a]形式的矩阵其中A∈VN,a∈VT∪{#}A\in V_N,a\in V_T\cup\{\#\}A∈VN,a∈VT∪{#}. 分析表元素M[A,a]M[A,a]M[A,a]中的内容为一条关于A的规则记为G’[P]中的编号表中以空白表示出错标志表明A不应该面临输入符号a. 预测分析表对不同LL(1)文法是不同的。
构造预测分析表的核心思想是假定 A→αA\rightarrow\alphaA→α 是A的一个候选式对 a∈Select(A→α)a\in Select\left(A\rightarrow\alpha\right)a∈Select(A→α) 应当将M[A,a]M[A,a]M[A,a]设置为 A→αA\rightarrow\alphaA→α. 那么当A在栈顶且a是当前输入符号时α\alphaα 应当被视为A的唯一匹配。 根据文法G’[P]和Select集构建预测分析表 3.3.8 递归下降分析器的设计
递归下降分析方法的主要思想是根据文法的产生式从开始符号开始自上而下进行分析。递归下降分析器的输入是词法分析输出的token文件通过分析检查输入的token串是否符合文法要求输出是语法树。递归下降语法分析器分析程序的接口如下图所示 构造递归下降分析程序时每个函数名是相应的非终结符函数体是根据规则右部符号串的结构编写的这组函数以相互递归的方式进行调用。当面对多个候选式时将根据下一个读入的输入符号和上述预测分析表采用合适的候选式。
语法分析程序所用的全局变量和需要调用的函数如下
TokenWord类实例语法分析器读入的一个单词符号Node语法树结点的数据结构包含成员变量token、X和children. token记录叶子结点对应的终结符信息X记录递归下降过程中非叶子结点对应的非终结符类型children记录该结点的孩子结点指针通过children是否为空判断是否是叶子结点root指向语法树根结点的指针DFS(Node*)深度优先搜索函数遍历语法树并打印match(TokenCode,Node*)当前Token是否匹配终结符若匹配则创建叶子结点反之提示Token与栈顶终结符不匹配P()起始非终结符递归函数递归分析的入口函数不参与后续递归创建语法树根结点VN枚举型表示非终结符的类型getTK()从输入的token串中取出一个单词符号指针迭代器后移一个单词isk()判断Token是否是终结符ksin,cos,tg,ctg,lg,lnisID()判断Token是否是标识符包括已定义常量如E、PI等CheckInput(TokenCode *First,TokenCode *Follow)检查Token是否在First集中若Token不在First集中则取调用getTK()取下一个Token直到新的Token在First集或Follow集包含#中以#作为结束符号syntaxError(Word, ErrorType)报告语法错误参数为错误发生的token信息和错误的类型。因为需要指出错误发生的位置所以在Word类中添加一个整型变量记录token所在行数。ErrorType错误类型有两种分别是终结符与当前输入符号不匹配和非终结符与当前输入符号不匹配S(Node*)…C_prime(Node*)对应非终结符的递归函数向下层传递本层的树结点信息。
虽然手工构造的递归下降分析器简单、直观但是因其递归调用次数多影响了分析器的效率故通常采用表驱动的预测分析法实现分析过程。表驱动的预测分析法显式维护一个状态栈和一个二维分析表具体原理和递归下降分析器类似这里不再展开讨论。
3.3.9 递归下降分析程序中错误处理方式的优化
语法分析器至少能判断出一个程序在语句构成上是否正确即如果源程序包括语法错误则必须指出某个错误的存在反之若程序中没有语法错误分析程序不应声称有错误存在。通常的错误处理程序试图给出一个有意义的错误信息尽可能地判断出错误发生位置。除了以上的最低要求外本文实现递归下降分析中的错误校正即试图从给出的不正确的程序中推断出正确的程序如跳过某些单词等。
设计过程遵循如下原则
1以发现错误为主校正错误为辅。我们希望语法分析器能从常见的语法错误中恢复并继续处理程序的剩余部分而不是发现错误就立即退出
2错误局部化选择一个适当的位置恢复分析过程。分析程序应尽可能多地分析代码更多地找到真实的错误而不是出现错误后马上停止分析即使跳过部分代码也应使语法分析程序跳过的语法成分最少
3准确报告应尽早给出错误发生的位置否则错误位置可能会丢失。减少重复信息避免出现错误级联问题。还应避免错误的无限循环等。
在自上而下语法分析中通常使用应急模式来进行错误恢复。应急模式不需要改变分析栈并且能够保证不会进入无限循环。其核心机制是为每个递归函数定义一个同步词法符号集在分析处理时将同步符号集作为参数传递给分析函数如果遇到错误分析程序就继续向前读入输入符号丢弃遇到的符号直到看到某个输入符号与同步符号集中的某个符号相同为止并从这里恢复分析。在做这种快速扫描时在某种程度上通过不生成新的出错信息来避免错误级联。
在基于预测分析表的递归下降程序中分析过程中出现下述两种情况说明出现了语法错误
1当前终结符与当前输入符号不相匹配 2非终结符与当前输入符号不相匹配。
第一种情况下可以直接将该终结符弹出。
第二种情况下可以把该非终结符的Follow集的所有符号放入该非终结符的同步符号集。在遇到错误时如果该符号在非终结符的同步符号集里该非终结符就弹出反之就跳过输入符号直至出现与当前非终结符匹配的符号或在同步符号集中的符号。实现方法是在每一个递归函数开始时调用CheckInput()函数来检查和处理错误。注意“#”应当在所有非终结符的同步符号集里。 注G’[P]产生式18中终结符id包含标识符和常量E、PI而产生式4的id仅是标识符因为常量不能被赋值二者应当加以区分。
3.3.10 结果分析
注输入1、2为正确程序输入3为错误程序。若通过语法检查则输出语法树语法树前编号为树的层数。错误程序3虽然未通过语法检查但检测并纠正了错误也构建了正确的语法树。
【输入1】
a3;
?1/sin(log(2,123.321))log(7z)^PI;【输出1】
Lexical analysis succeeded!
Result of lexical analysis is:
0,id,a
0,,
0,i,3
0,;,
1,?,
1,i,1
1,/,
1,sin,
1,(,
1,log,
1,(,
1,i,2
1,,,
1,i,123.321
1,),
1,),
1,,
1,log,
1,(,
1,i,7
1,,
1,id,z
1,),
1,^,
1,PI,
1,;,
LL(1) Syntax analysis succeeded!
LL(1) Syntax tree:
(1) P--S
(2) S--D
(3) D--id
(3) D--
(3) D--E
(4) E--A
(5) A--B
(6) B--F
(7) F--i
(6) B--B
(5) A--A
(4) E--E
(2) S--;
(2) S--S
(1) P--?
(1) P--E
(2) E--A
(3) A--B
(4) B--F
(5) F--i
(4) B--B
(3) A--A
(4) A--/
(4) A--B
(5) B--F
(6) F--C
(7) C--sin
(7) C--(
(7) C--E
(8) E--A
(9) A--B
(10) B--F
(11) F--C
(12) C--log
(12) C--(
(12) C--E
(13) E--A
(14) A--B
(15) B--F
(16) F--i
(15) B--B
(14) A--A
(13) E--E
(12) C--C
(13) C--,
(13) C--E
(14) E--A
(15) A--B
(16) B--F
(17) F--i
(16) B--B
(15) A--A
(14) E--E
(13) C--)
(10) B--B
(9) A--A
(8) E--E
(7) C--)
(5) B--B
(4) A--A
(2) E--E
(3) E--
(3) E--A
(4) A--B
(5) B--F
(6) F--C
(7) C--log
(7) C--(
(7) C--E
(8) E--A
(9) A--B
(10) B--F
(11) F--i
(10) B--B
(9) A--A
(8) E--E
(9) E--
(9) E--A
(10) A--B
(11) B--F
(12) F--id
(11) B--B
(10) A--A
(9) E--E
(7) C--C
(8) C--)
(5) B--B
(6) B--^
(6) B--F
(7) F--PI
(6) B--B
(4) A--A
(3) E--E
(1) P--;【输入2】
a0.1;
b3;
?1*log(2,3)log(sin(4));【输出2】
Lexical analysis succeeded!
Result of lexical analysis is:
0,id,a
0,,
0,i,0.1
0,;,
1,id,b
1,,
1,i,3
1,;,
2,?,
2,i,1
2,*,
2,log,
2,(,
2,i,2
2,,,
2,i,3
2,),
2,,
2,log,
2,(,
2,sin,
2,(,
2,i,4
2,),
2,),
2,;,
LL(1) Syntax analysis succeeded!
LL(1) Syntax tree:
(1) P--S
(2) S--D
(3) D--id
(3) D--
(3) D--E
(4) E--A
(5) A--B
(6) B--F
(7) F--i
(6) B--B
(5) A--A
(4) E--E
(2) S--;
(2) S--S
(3) S--D
(4) D--id
(4) D--
(4) D--E
(5) E--A
(6) A--B
(7) B--F
(8) F--i
(7) B--B
(6) A--A
(5) E--E
(3) S--;
(3) S--S
(1) P--?
(1) P--E
(2) E--A
(3) A--B
(4) B--F
(5) F--i
(4) B--B
(3) A--A
(4) A--*
(4) A--B
(5) B--F
(6) F--C
(7) C--log
(7) C--(
(7) C--E
(8) E--A
(9) A--B
(10) B--F
(11) F--i
(10) B--B
(9) A--A
(8) E--E
(7) C--C
(8) C--,
(8) C--E
(9) E--A
(10) A--B
(11) B--F
(12) F--i
(11) B--B
(10) A--A
(9) E--E
(8) C--)
(5) B--B
(4) A--A
(2) E--E
(3) E--
(3) E--A
(4) A--B
(5) B--F
(6) F--C
(7) C--log
(7) C--(
(7) C--E
(8) E--A
(9) A--B
(10) B--F
(11) F--C
(12) C--sin
(12) C--(
(12) C--E
(13) E--A
(14) A--B
(15) B--F
(16) F--i
(15) B--B
(14) A--A
(13) E--E
(12) C--)
(10) B--B
(9) A--A
(8) E--E
(7) C--C
(8) C--)
(5) B--B
(4) A--A
(3) E--E
(1) P--;【输入3】
a4;?a1-log(02**3)【输出3】
Lexical analysis succeeded!
Result of lexical analysis is:
0,id,a
0,,
0,i,4
0,;,
0,?,
0,id,a
0,,
0,,
0,i,1
0,-,
0,log,
0,(,
0,i,0
0,i,2
0,*,
0,*,
0,i,3
0,),ERROR(1): NONTERMINATOR_NOT_MATCH_TOKEN 0,,
ERROR(1): NONTERMINATOR_NOT_MATCH_TOKEN 0,i,2
ERROR(1): NONTERMINATOR_NOT_MATCH_TOKEN 0,*,
ERROR(1): NONTERMINATOR_NOT_MATCH_TOKEN 0,#,
LL(1) Syntax analysis failed!图 3-3 表达式 a3;?1/sin(log(2,123.321))log(7z)^PI; 的语法树 图 3-4 表达式 a0.1;b3;?1*log(2,3)log(sin(4)); 的语法树
3.3.11 代码实现
1. #include LexicalAnalyze.h
2.
3. enum VN {
4. VN_UNDEFINED,
5. VN_P,
6. VN_S,
7. VN_D,
8. VN_E,
9. VN_E_prime,
10. VN_A,
11. VN_A_prime,
12. VN_B,
13. VN_B_prime,
14. VN_F,
15. VN_C,
16. VN_C_prime
17. };
18.
19. enum ERRORTYPE {
20. TERMINATOR_NOT_MATCH_TOKEN, // 栈顶终结符不匹配当前输入符号
21. NONTERMINATOR_NOT_MATCH_TOKEN // 栈顶非终结符不匹配当前输入符号
22. };
23.
24. typedef class Node {
25. public:
26. explicit Node(VN x, Word *token nullptr) : X(x), token(token) {}
27.
28. string vn_to_str();
29.
30. VN X;
31. Word *token;
32. vectorNode * children;
33. } *TreeNode;
34.
35. class LL1 : public LexicalAnalyze {
36. public:
37. explicit LL1(const string pg);
38.
39. protected:
40. TreeNode root;
41.
42. private:
43. bool error;
44. int tree_level;
45. vectorWord::iterator Token;
46.
47. bool isk();
48.
49. bool isID();
50.
51. void getTK();
52.
53. void show_tree();
54.
55. void show_result();
56.
57. void syntaxError(ERRORTYPE et);
58.
59. void match(TokenCode tc, TreeNode parent);
60.
61. bool isInSet(const TokenCode *Set, int size);
62.
63. void CheckInput(TokenCode *First, int First_size, TokenCode *Follow, int Follow_size);
64.
65. void DFS(TreeNode pre_tptr, TreeNode next_tptr);
66.
67. void P();
68.
69. void S(TreeNode parent);
70.
71. void D(TreeNode parent);
72.
73. void E(TreeNode parent);
74.
75. void E_prime(TreeNode parent);
76.
77. void A(TreeNode parent);
78.
79. void A_prime(TreeNode parent);
80.
81. void B(TreeNode parent);
82.
83. void B_prime(TreeNode parent);
84.
85. void F(TreeNode parent);
86.
87. void C(TreeNode parent);
88.
89. void C_prime(TreeNode parent);
90. };
91.
92.
93. LL1::LL1(const string pg) :
94. LexicalAnalyze(pg), root(nullptr), error(false), tree_level(0) {
95. if (words.empty()) {
96. cout \033[31mYour program is empty!\033[0m endl;
97. exit(0);
98. }
99. words.emplace_back(line, TC_EOF);
100. Token words.begin();
101. P();
102. show_result();
103. }
104.
105. bool LL1::isk() {
106. TokenCode tc (*Token).tcode;
107. TokenCode k[] {TC_SIN, TC_COS, TC_TG, TC_CTG, TC_LG, TC_LN};
108. for (auto i : k) {
109. if (tc i)
110. return true;
111. }
112. return false;
113. }
114.
115. bool LL1::isID() {
116. TokenCode tc (*Token).tcode;
117. TokenCode ID[3] {TC_ID, TC_E, TC_PI};
118. for (auto i : ID) {
119. if (tc i)
120. return true;
121. }
122. return false;
123. }
124.
125. void LL1::getTK() {
126. if (Token words.end())
127. Token;
128. }
129.
130. void LL1::show_tree() {
131. tree_level 0;
132. cout \033[33mLL(1) Syntax tree:\033[0m endl;
133. for (auto child:root-children)
134. DFS(root, child);
135. }
136.
137. void LL1::show_result() {
138. if (error) {
139. show_tree();
140. cout \033[31mLL(1) Syntax analysis failed!\033[0m endl;
141. } else {
142. cout \033[32mLL(1) Syntax analysis succeeded!\033[0m endl;
143. show_tree();
144. }
145. }
146.
147. void LL1::syntaxError(ERRORTYPE et) {
148. if (et TERMINATOR_NOT_MATCH_TOKEN)
149. printf(\033[31mERROR(%d): TERMINATOR_NOT_MATCH_TOKEN\033[0m , et);
150. else
151. printf(\033[31mERROR(%d): NONTERMINATOR_NOT_MATCH_TOKEN\033[0m , et);
152. (*Token).print();
153. error true;
154. if ((*Token).tcode TC_EOF) {
155. cout \033[31mLL(1) Syntax analysis failed!\033[0m endl;
156. exit(0);
157. }
158. }
159.
160. void LL1::match(TokenCode tc, TreeNode parent) {
161. if ((*Token).tcode tc) {
162. auto tptr new Node(VN_UNDEFINED, (*Token));
163. parent-children.push_back(tptr);
164. getTK();
165. } else
166. syntaxError(TERMINATOR_NOT_MATCH_TOKEN);
167. }
168.
169. bool LL1::isInSet(const TokenCode *Set, int size) {
170. TokenCode tc (*Token).tcode;
171. for (int count 0; count size; count) {
172. if (tc Set[count])
173. return true;
174. }
175. return false;
176. }
177.
178. void LL1::CheckInput(TokenCode *First, int First_size, TokenCode *Follow, int Follow_size) {
179. if (!isInSet(First, First_size)) {
180. syntaxError(NONTERMINATOR_NOT_MATCH_TOKEN);
181. while (!isInSet(Follow, Follow_size) !isInSet(First, First_size) (*Token).tcode ! TC_EOF)
182. getTK();
183. }
184. }
185.
186. void LL1::DFS(TreeNode pre_tptr, TreeNode next_tptr) {
187. tree_level;
188. if (next_tptr-children.empty()) {
189. if (next_tptr-token nullptr) // VN--epsilon
190. printf((%d) %s--%s\n,
191. tree_level,
192. pre_tptr-vn_to_str().c_str(),
193. next_tptr-vn_to_str().c_str());
194. else // VN--VT
195. printf((%d) %s--\033[36m%s\033[0m\n,
196. tree_level,
197. pre_tptr-vn_to_str().c_str(),
198. next_tptr-token-tc_to_str().c_str());
199. tree_level--;
200. return;
201. }
202. printf((%d) %s--%s\n,
203. tree_level,
204. pre_tptr-vn_to_str().c_str(),
205. next_tptr-vn_to_str().c_str());
206. for (auto child:next_tptr-children)
207. DFS(next_tptr, child);
208. tree_level--;
209. }
210.
211. void LL1::P() {
212. root new Node(VN_P);
213. TokenCode tc;
214. TokenCode First[] {TC_ID, TC_RUN};
215. TokenCode SYN[] {};
216. CheckInput(First, sizeof(First) / sizeof(TokenCode), SYN, sizeof(SYN) / sizeof(TokenCode));
217. tc (*Token).tcode;
218. if (tc TC_ID || tc TC_RUN) {
219. S(root);
220. match(TC_RUN, root);
221. E(root);
222. match(TC_SEP, root);
223. }
224. }
225.
226. void LL1::S(TreeNode parent) {
227. auto tptr new Node(VN_S);
228. TokenCode tc;
229. TokenCode First[] {TC_ID, TC_RUN};
230. TokenCode SYN[] {};
231. parent-children.push_back(tptr);
232. CheckInput(First, sizeof(First) / sizeof(TokenCode), SYN, sizeof(SYN) / sizeof(TokenCode));
233. tc (*Token).tcode;
234. if (tc TC_ID) {
235. D(tptr);
236. match(TC_SEP, tptr);
237. S(tptr);
238. } else if (tc TC_RUN)
239. return;
240. }
241.
242. void LL1::D(TreeNode parent) {
243. auto tptr new Node(VN_D);
244. TokenCode tc;
245. TokenCode First[] {TC_ID};
246. TokenCode SYN[] {TC_SEP};
247. parent-children.push_back(tptr);
248. CheckInput(First, sizeof(First) / sizeof(TokenCode), SYN, sizeof(SYN) / sizeof(TokenCode));
249. tc (*Token).tcode;
250. if (tc TC_ID) {
251. match(TC_ID, tptr);
252. match(TC_EQU, tptr);
253. E(tptr);
254. }
255. }
256.
257. void LL1::E(TreeNode parent) {
258. auto tptr new Node(VN_E);
259. TokenCode tc;
260. TokenCode First[] {TC_LBR, TC_NUM,
261. TC_ID, TC_E, TC_PI,
262. TC_LOG,
263. TC_SIN, TC_COS, TC_TG, TC_CTG, TC_LG, TC_LN};
264. TokenCode SYN[] {TC_RBR, TC_SEP, TC_CMA};
265. parent-children.push_back(tptr);
266. CheckInput(First, sizeof(First) / sizeof(TokenCode), SYN, sizeof(SYN) / sizeof(TokenCode));
267. tc (*Token).tcode;
268. if (tc TC_LBR || tc TC_NUM || isID() || tc TC_LOG || isk()) {
269. A(tptr);
270. E_prime(tptr);
271. }
272. }
273.
274. void LL1::E_prime(TreeNode parent) {
275. auto tptr new Node(VN_E_prime);
276. TokenCode tc;
277. TokenCode First[] {TC_ADD, TC_SUB, TC_RBR, TC_SEP, TC_CMA};
278. TokenCode SYN[] {};
279. parent-children.push_back(tptr);
280. CheckInput(First, sizeof(First) / sizeof(TokenCode), SYN, sizeof(SYN) / sizeof(TokenCode));
281. tc (*Token).tcode;
282. if (tc TC_ADD) {
283. match(TC_ADD, tptr);
284. A(tptr);
285. E_prime(tptr);
286. } else if (tc TC_SUB) {
287. match(TC_SUB, tptr);
288. A(tptr);
289. E_prime(tptr);
290. } else if (tc TC_RBR || tc TC_SEP || tc TC_CMA)
291. return;
292. }
293.
294. void LL1::A(TreeNode parent) {
295. auto tptr new Node(VN_A);
296. TokenCode tc;
297. TokenCode First[] {TC_LBR, TC_NUM,
298. TC_ID, TC_E, TC_PI,
299. TC_LOG,
300. TC_SIN, TC_COS, TC_TG, TC_CTG, TC_LG, TC_LN};
301. TokenCode SYN[] {TC_ADD, TC_SUB, TC_RBR, TC_SEP, TC_CMA};
302. parent-children.push_back(tptr);
303. CheckInput(First, sizeof(First) / sizeof(TokenCode), SYN, sizeof(SYN) / sizeof(TokenCode));
304. tc (*Token).tcode;
305. if (tc TC_LBR || tc TC_NUM || isID() || tc TC_LOG || isk()) {
306. B(tptr);
307. A_prime(tptr);
308. }
309. }
310.
311. void LL1::A_prime(TreeNode parent) {
312. auto tptr new Node(VN_A_prime);
313. TokenCode tc;
314. TokenCode First[] {TC_ADD, TC_SUB, TC_TIM, TC_DIV,
315. TC_RBR, TC_SEP, TC_CMA};
316. TokenCode SYN[] {};
317. parent-children.push_back(tptr);
318. CheckInput(First, sizeof(First) / sizeof(TokenCode), SYN, sizeof(SYN) / sizeof(TokenCode));
319. tc (*Token).tcode;
320. if (tc TC_TIM) {
321. match(TC_TIM, tptr);
322. B(tptr);
323. A_prime(tptr);
324. } else if (tc TC_DIV) {
325. match(TC_DIV, tptr);
326. B(tptr);
327. A_prime(tptr);
328. } else if (tc TC_ADD || tc TC_SUB || tc TC_RBR || tc TC_SEP || tc TC_CMA)
329. return;
330. }
331.
332. void LL1::B(TreeNode parent) {
333. auto tptr new Node(VN_B);
334. TokenCode tc;
335. TokenCode First[] {TC_LBR, TC_NUM,
336. TC_ID, TC_E, TC_PI,
337. TC_LOG,
338. TC_SIN, TC_COS, TC_TG, TC_CTG, TC_LG, TC_LN};
339. TokenCode SYN[] {TC_ADD, TC_SUB, TC_TIM, TC_DIV, TC_RBR, TC_SEP, TC_CMA};
340. parent-children.push_back(tptr);
341. CheckInput(First, sizeof(First) / sizeof(TokenCode), SYN, sizeof(SYN) / sizeof(TokenCode));
342. tc (*Token).tcode;
343. if (tc TC_LBR || tc TC_NUM || isID() || tc TC_LOG || isk()) {
344. F(tptr);
345. B_prime(tptr);
346. }
347. }
348.
349. void LL1::B_prime(TreeNode parent) {
350. auto tptr new Node(VN_B_prime);
351. TokenCode tc;
352. TokenCode First[] {TC_ADD, TC_SUB, TC_TIM, TC_DIV, TC_POW,
353. TC_RBR, TC_SEP, TC_CMA};
354. TokenCode SYN[] {};
355. parent-children.push_back(tptr);
356. CheckInput(First, sizeof(First) / sizeof(TokenCode), SYN, sizeof(SYN) / sizeof(TokenCode));
357. tc (*Token).tcode;
358. if (tc TC_POW) {
359. match(TC_POW, tptr);
360. F(tptr);
361. B_prime(tptr);
362. } else if (tc TC_ADD || tc TC_SUB || tc TC_TIM || tc TC_DIV || tc TC_RBR || tc TC_SEP ||
363. tc TC_CMA)
364. return;
365. }
366.
367. void LL1::F(TreeNode parent) {
368. auto tptr new Node(VN_F);
369. TokenCode tc;
370. TokenCode First[] {TC_LBR, TC_NUM,
371. TC_ID, TC_E, TC_PI,
372. TC_LOG,
373. TC_SIN, TC_COS, TC_TG, TC_CTG, TC_LG, TC_LN};
374. TokenCode SYN[] {TC_ADD, TC_SUB, TC_TIM, TC_DIV, TC_POW, TC_RBR, TC_SEP, TC_CMA};
375. parent-children.push_back(tptr);
376. CheckInput(First, sizeof(First) / sizeof(TokenCode), SYN, sizeof(SYN) / sizeof(TokenCode));
377. tc (*Token).tcode;
378. if (tc TC_LBR) {
379. match(TC_LBR, tptr);
380. E(tptr);
381. match(TC_RBR, tptr);
382. } else if (tc TC_LOG || isk()) {
383. C(tptr);
384. } else if (isID()) {
385. match(tc, tptr);
386. } else if (tc TC_NUM) {
387. match(TC_NUM, tptr);
388. }
389. }
390.
391. void LL1::C(TreeNode parent) {
392. auto tptr new Node(VN_C);
393. TokenCode tc;
394. TokenCode First[] {TC_LOG, TC_SIN, TC_COS, TC_TG, TC_CTG, TC_LG, TC_LN};
395. TokenCode SYN[] {TC_ADD, TC_SUB, TC_TIM, TC_DIV, TC_POW, TC_RBR, TC_SEP, TC_CMA};
396. parent-children.push_back(tptr);
397. CheckInput(First, sizeof(First) / sizeof(TokenCode), SYN, sizeof(SYN) / sizeof(TokenCode));
398. tc (*Token).tcode;
399. if (tc TC_LOG) {
400. match(TC_LOG, tptr);
401. match(TC_LBR, tptr);
402. E(tptr);
403. C_prime(tptr);
404. } else if (isk()) {
405. match(tc, tptr);
406. match(TC_LBR, tptr);
407. E(tptr);
408. match(TC_RBR, tptr);
409. }
410. }
411.
412. void LL1::C_prime(TreeNode parent) {
413. auto tptr new Node(VN_C_prime);
414. TokenCode tc;
415. TokenCode First[] {TC_RBR, TC_CMA};
416. TokenCode SYN[] {TC_ADD, TC_SUB, TC_TIM, TC_DIV, TC_POW, TC_SEP};
417. parent-children.push_back(tptr);
418. CheckInput(First, sizeof(First) / sizeof(TokenCode), SYN, sizeof(SYN) / sizeof(TokenCode));
419. tc (*Token).tcode;
420. if (tc TC_RBR) {
421. match(TC_RBR, tptr);
422. } else if (tc TC_CMA) {
423. match(TC_CMA, tptr);
424. E(tptr);
425. match(TC_RBR, tptr);
426. }
427. }
428.
429.
430. string Node::vn_to_str() {
431. string vn_name;
432. switch (X) {
433. case VN_UNDEFINED:
434. vn_name VN_UNDEFINED;
435. break;
436. case VN_P:
437. vn_name P;
438. break;
439. case VN_S:
440. vn_name S;
441. break;
442. case VN_D:
443. vn_name D;
444. break;
445. case VN_E:
446. vn_name E;
447. break;
448. case VN_E_prime:
449. vn_name E;
450. break;
451. case VN_A:
452. vn_name A;
453. break;
454. case VN_A_prime:
455. vn_name A;
456. break;
457. case VN_B:
458. vn_name B;
459. break;
460. case VN_B_prime:
461. vn_name B;
462. break;
463. case VN_F:
464. vn_name F;
465. break;
466. case VN_C:
467. vn_name C;
468. break;
469. case VN_C_prime:
470. vn_name C;
471. break;
472. }
473. return vn_name;
474. }
475.
476.
477. int main() {
478. string program a4;?a1-log(2*3,4);;
479. LL1 ll1(program);
480. return 0;
481. }
第4章 语义分析
4.1 实验目的
1、为初等函数运算语言设计语义分析器 2、掌握语义分析器的设计和开发方法加深对语义分析原理的理解。
4.2 实验内容及要求
1、根据初等函数运算语言运算法则将一个完整的、合法的初等函数运算语言的程序的计算结果计算出来
2、为初等函数运算语言设计恰当的三地址代码并编写编译器将一个完整的、合法的初等函数运算语言的程序转换成一个四元式序列。将该中间代码序列转换为Intel 80x86汇编指令并在汇编器中将其转换成机器指令打包成可执行文件
3、完成不完整的初等函数运算语言程序的计算将所有能够计算的表达式都计算出相应的值输出约减后的表达式。
4.3 实验过程
4.3.1 引言
语法制导翻译法的基本思想是对文法中的每个产生式都附加上一个语义动作或语义子程序在执行语法分析的过程中每当使用一条产生式进行推导或规约时就执行相应产生式的语义动作。
语法制导翻译技术分为自下而上的语法制导翻译和自上向下的语法制导翻译。可以自下而上一次遍历语法树的所有结点从而计算出各个结点的综合属性值也可以自上而下一次遍历语法树的所有结点从而计算出各个结点的继承属性值。在第三章中我们通过改写非LL(1)文法使之满足LL(1)条件接着构造手工编码的递归下降分析器自上而下地生成语法树。然而由于对文法进行了左递归改造使得非左递归的新文法的翻译模式中既含有综合属性也含有继承属性以至于属性间形成了复杂的依赖关系一次遍历语法树不能计算出所有属性的值多个属性的计算顺序就成了一个问题。如果在语法分析阶段使用自下向上的LR分析法那么就可避免在文法符号中形成继承属性LR分析器构建语法树进行规约的同时可以调用对应的语义规则进行属性的计算大大降低了语法制导翻译和中间代码生成的难度也使得语义规则变得更加清晰明确。
LR分析法是一种自下而上进行规范规约的语法分析方法。相较于递归下降分析法LR分析法的限制少得多其适用于大多数无二义性上下文无关文法描述的语言虽然存在着非LR的上下文无关文法但一般而言常见的程序设计语言构造都可以避免使用这样的文法。LR(0)分析法存在“移进-规约”和“规约-规约”冲突而SLR(1)分析法在LR(0)分析的基础上前瞻一个输入符号不仅解决了LR(0)的局限性还分析表构造简单、易于实现有较高的实用价值。LR(1)分析法分析能力最强但分析表体积庞大构造代价高。故本节选用SLR(1)分析法进行语法分析、构造语法树并进行语法制导翻译。
4.3.2 LR分析器的工作原理和过程
LR分析法是一种规范规约分析法。LR分析法的基本思想是在规范规约分析过程中根据分析栈中记录的已移进和规约出的整个符号串历史和根据使用的规则推测未来可能遇到的输入符号展望以及现实读到的符号这三个方面的信息来确定分析栈栈顶的符号串是否形成句柄。
LR分析器由分析栈、分析表和总控程序三部分组成。分析栈用来存放分析过程中的历史和展望信息LR分析法将历史和展望信息抽象成状态放入分析栈中。LR分析表是分析器和核心部分一张LR分析表由分析动作表和状态转换表两部分组成它们都是二维数组。
状态转换表元素GOTO[Si,X]规定了当前状态Si面临文法符号X时应转移到的下一个状态。分析动作表元素ACTION[Si,a]规定了当前状态Si面临输入符号a时应执行的动作 总控程序的算法如下 输入输入串W和LR分析表 输出若W是句子则得到W的自下而上分析成功的信息否则输出错误信息 算法初始化时初始状态S0在分析栈栈顶输入串“W#”的第一个符号读入a中
while (ACTION[S,a]!acc) {if (ACTION[S,a]Si) {Si和a进栈;读入下一个a;}else if (ACTION[S,a]rj) {使用规则j. A-α 规约;将 |α| 个状态和输入符号退栈;当前栈顶状态为S将A和GOTO[S,A]进栈;}else if (ACTION[S,a]ERROR)error();
}4.3.3 LR(0)分析法
对一个文法G可以构造一个DFA来识别G的所有规范句型的活前缀在此基础上将其转换成LR分析表。在LR分析的任何时候栈里的文法符号自栈底向上应该构成活前缀把输入串的剩余部分匹配于其后即应成为规范句型。因此在规范规约过程中的任何时刻只要已分析过的部分一直保持为可规约成某个活前缀就表明输入串已被分析过的部分没有发现语法错误。加上输入串的剩余部分恰好就是活前缀所属的规范句型。一旦栈顶出现句柄就被规约成某个产生式左部符号所以活前缀不包括句柄之后的任何符号。用“项目”来表示分析过程中已经分析过的部分。可根据项目中圆点所在位置和后继符号类型把项目分为归约项目、接受项目、移进项目和待约项目分别型如 A→α∙A\rightarrow\alpha\bulletA→α∙ 、S′→α∙S^\prime\rightarrow\alpha\bulletS′→α∙ 、A→α∙aβA\rightarrow\alpha\bullet a\ \betaA→α∙a β 、A→α∙BβA\rightarrow\alpha\bullet B\ \betaA→α∙B β. 注意对于规则 A→ϵA\rightarrow\epsilonA→ϵ 仅有LR(0)项目A→∙A\rightarrow\bulletA→∙.
由第三章的分析可知含有左递归的描述该语言的文法为G’[P] 其中终结符k代表sin、cos、tg、ctg、lg、ln、log的集合。注意id’不包含常量E和PI但id包含E、PI以及其他已声明的标识符。文法G’的开始符号P不在任何产生式的右部在规约的过程中可以分清是否已规约到文法的最初开始符故无需对原文法进行拓广。
可以列出拓广文法的所有项目构造LR(0)项目集规范族在项目集中所有的LR(0)项目识别的活前缀时相同的通过构造其NFA再利用子集法确定化为识别活前缀的DFA但该方法工作量大。对于NFA确定化为DFA可利用闭包函数和状态转换函数的概念直接生成DFA从而避免非确定的ϵ\epsilonϵ转移。
闭包函数的定义设I是拓广文法G’的一个LR(0)项目集定义和构造I的闭包CLOSURE(I)I中的任何一个项目都属于CLOSURE(I)若 A→α∙BβA\rightarrow\alpha\bullet B\betaA→α∙Bβ 属于CLOSURE(I)则每一形如 B→∙γB\rightarrow\bullet\gammaB→∙γ 的项目也属于CLOSURE(I)重复上述步骤直到CLOSURE(I)不再增大为止。
状态转移函数的定义设X为一文法符号 通过闭包函数CLOSURE和状态转移函数GO很容易构造出文法G’的识别文法规范句型活前缀的DFA. 其步骤如下
1求 CLOSURE({S′→∙S})CLOSURE\left(\{S^\prime\rightarrow\bullet S\}\right)CLOSURE({S′→∙S}) 得到初态项目集规范族 2对初态项目集或其他已构造的项目集应用状态转移函数GO(I,X)求出新的项目集后继状态 3重复步骤2直到不出现新的项目集为止
整个构造LR(0)项目集规范族和识别活前缀的DFA的过程可以描述为算法3.10. ⚠️ 接下来的内容皆为移花接木内容与本文完全不符但解题流程正确 ⚠️ 接下来的内容皆为移花接木内容与本文完全不符但解题流程正确 ⚠️ 接下来的内容皆为移花接木内容与本文完全不符但解题流程正确 ✅ 若要完成此部分请参考刘坚老师的《编译原理基础》 构造LR(0)项目集规范族和识别活前缀的DFA
I0{0,5,9,10}
I1GO(I0,S){1}
I2GO(I0,D){6}
I3GO(I0,id){11}
I4GO(I1,?){2,14,18,22,24,28,32,34,38,40,44,46,48,50,55}
I5GO(I2,;){5,7,10}
I6GO(I3,){12,14,18,22,24,28,32,34,38,40,44,46,48,50,55}
I7GO(I4,E){3,15,19}
I8GO(I4,A){23,25,29}
I9GO(I4,B){33,35}
I10GO(I4,F){39}
I11GO(I4,(){41}
I12GO(I4,C){45}
I13GO(I4,id){47}
I14GO(I4,i){49}
I15GO(I4,k){51}
I16GO(I4,log){51,56}
I2GO(I5,D){6}
I17GO(I5,S){8}
I3GO(I5,id){11}
I18GO(I6,E){13,15,19}
I8GO(I6,A){23,25,29}
I9GO(I6,B){33,35}
I10GO(I6,F){39}
I11GO(I6,(){41}
I12GO(I6,C){45}
I13GO(I6,id){47}
I14GO(I6,i){49}
I15GO(I6,k){51}
I16GO(I6,log){51,56}
I19GO(I7,;){4}
I20GO(I7,){16,24,28,32,34,38,40,44,46,48,50,55}
I21GO(I7,-){20,24,28,32,34,38,40,44,46,48,50,55}
I22GO(I8,*){26,34,38,40,44,46,48,50,55}
I23GO(I8,/){30,34,38,40,44,46,48,50,55}
I24GO(I9,^){36,40,44,46,48,50,55}
I25GO(I11,E){42}
I26GO(I15,(){14,18,22,24,28,32,34,38,40,44,46,48,50,52,55}
I26GO(I16,(){14,18,22,24,28,32,34,38,40,44,46,48,50,52,55,57}
I20GO(I18,){16,24,28,32,34,38,40,44,46,48,50,55}
I21GO(I18,-){20,24,28,32,34,38,40,44,46,48,50,55}
I27GO(I20,A){17,25,29}
I9GO(I20,B){33,35}
I10GO(I20,F){39}
I28GO(I20,(){14,18,22,24,28,32,34,38,40,41,44,46,48,50,55}
I12GO(I20,C){45}
I13GO(I20,id){47}
I14GO(I20,i){49}
I15GO(I20,k){51}
I16GO(I20,k){51,56}
I29GO(I21,A){21,25,29}
I9GO(I21,B){33,35}
I10GO(I21,F){39}
I28GO(I21,(){14,18,22,24,28,32,34,38,40,41,44,46,48,50,55}
I12GO(I21,C){45}
I13GO(I21,id){47}
I14GO(I21,i){49}
I15GO(I21,k){51}
I16GO(I21,k){51,56}
I30GO(I22,B){27,35}
I10GO(I22,F){39}
I28GO(I22,(){14,18,22,24,28,32,34,38,40,41,44,46,48,50,55}
I12GO(I22,C){45}
I13GO(I22,id){47}
I14GO(I22,i){49}
I15GO(I22,k){51}
I16GO(I22,k){51,56}
I31GO(I23,B){31,35}
I10GO(I23,F){39}
I28GO(I23,(){14,18,22,24,28,32,34,38,40,41,44,46,48,50,55}
I12GO(I23,C){45}
I13GO(I23,id){47}
I14GO(I23,i){49}
I15GO(I23,k){51}
I16GO(I23,k){51,56}
I32GO(I24,F){37}
I28GO(I24,(){14,18,22,24,28,32,34,38,40,41,44,46,48,50,55}
I12GO(I24,C){45}
I13GO(I24,id){47}
I14GO(I24,i){49}
I15GO(I24,k){51}
I16GO(I24,k){51,56}
I33GO(I25,)){43}
I34GO(I26,E){15,19,53,58}
I22GO(I27,*){26,34,38,40,44,46,48,50,55}
I23GO(I27,/){30,34,38,40,44,46,48,50,55}
I35GO(I28,E){15,19,42}
I36GO(I28,A){25,29}
I9GO(I28,B){33,35}
I10GO(I28,F){39}
I12GO(I28,C){45}
I13GO(I28,id){47}
I14GO(I28,i){49}
I15GO(I28,k){51}
I16GO(I28,k){51,56}
I22GO(I29,*){26,34,38,40,44,46,48,50,55}
I23GO(I29,/){30,34,38,40,44,46,48,50,55}
I37GO(I30,^){36,40,44,46,48,50,55}
I37GO(I31,^){36,40,44,46,48,50,55}
I20GO(I34,){16,24,28,32,34,38,40,44,46,48,50,55}
I21GO(I34,-){20,24,28,32,34,38,40,44,46,48,50,55}
I38GO(I34,)){54}
I39GO(I34,,){14,18,22,24,28,34,38,40,44,46,48,50,55,59}
I20GO(I35,){16,24,28,32,34,38,40,44,46,48,50,55}
I21GO(I35,-){20,24,28,32,34,38,40,44,46,48,50,55}
I33GO(I35,)){43}
I22GO(I36,*){26,34,38,40,44,46,48,50,55}
I23GO(I36,/){30,34,38,40,44,46,48,50,55}
I40GO(I37,F){37}
I28GO(I37,(){14,18,22,24,28,32,34,38,40,41,44,46,48,50,55}
I12GO(I37,C){45}
I13GO(I37,id){47}
I14GO(I37,i){49}
I15GO(I37,k){51}
I16GO(I37,k){51,56}
I41GO(I39,E){15,19,60}
I36GO(I39,A){23,25,29}
I9GO(I39,B){33,35}
I10GO(I39,F){39}
I12GO(I39,C){45}
I13GO(I39,id){47}
I14GO(I39,i){49}
I15GO(I39,k){51}
I16GO(I39,k){51,56}
I20GO(I41,){16,24,28,32,34,38,40,44,46,48,50,55}
I21GO(I41,-){20,24,28,32,34,38,40,44,46,48,50,55}
I42GO(I41,)){61}在这42个项目集中I1、I2、I7、I19、I20、25、I32、I41等项目集中存在“移进-归约”冲突和“规约-归约”冲突现通过前瞻一个符号的SLR(1)分析法解决如上冲突。
一般而言若一个LR(0)项目集I中有m个移进项目和n个归约项目时 4.3.4 语法制导翻译器的设计与实现
四元式主要由4部分组成
(i)(op,arg1,arg2,result)(i)\ \ (op,arg1,arg2,result)(i) (op,arg1,arg2,result)
其中op是运算符arg1arg2分别是第一和第二个运算对象。当op是一目运算时常常将运算对象定义为arg1. 编译系统中有时将四元式表示成另一种更直观、更易理解的形式——三地址代码。三地址代码形式突出表现了每一个四元式中参加运算的对象和结果变量这种表示形式有利于中间代码的优化和目标代码的生成。
在自下而上翻译过程中当分析栈栈顶形成句柄执行规约时调用相应的语义动作语法分析栈与语义分析栈同步操作。现对文法G’[P]在规约的同时执行的语义子程序中设置的语义变量、语义过程及函数作出如下规定
1对非终结符A定义语义变量A.place. A.place表示存放A值的变量名在符号表中的入口地址或临时变量名的整数码
2定义语义函数newtemp()其功能是产生一个新的临时变量名字如t1、t2等。具体实现时每产生一个ti就及时送入临时变量所在符号表
3定义语义过程emit(t agr1 op arg2)其功能为产生一个四元式并及时填入四元式表中
4定义语义过程lookup(id.name)其功能是审查id.name是否出现在用户声明变量的符号表中在则返回其指针否则返回NULL
5定义语义过程InsertVar(id)其功能是向用户声明变量的符号表中插入变量并返回插入变量的地址
6定义语义过程print(A.val)其功能是打印相应终结符或非终结符的值
7建立两张符号表分别保存用户声明的变量和临时变量符号表包括变量名和变量的对应值。 4.3.5 符号表的组织与管理
符号表的组织与管理使用C关联式容器map. map的内部结构是红黑树根据key自动排序key唯一查找速度快。选用 string 字符串作为键的类型保存变量名或临时变量名选用 float 单精度浮点数作为值的类型保存变量对应的数值。
通过调用map容器类的默认构造函数可以创建出一个空的map容器
std::mapstd::string, floatcharT;
在C中使用insert()或效率更高的emplace()函数向charT中插入键值对使用find(key)函数。在map容器中查找键为key的键值对如果成功找到则返回指向该键值对的双向迭代器反之则返回和end()方法一样的迭代器。
4.3.6 目标代码生成器的设计与实现
目标代码生成器的主要任务是把源程序的中间代码形式四元式变换为依赖于具体机器的等价的目标代码其输入是编译前端输出的信息包括中间代码或优化后的中间代码以及带有存储信息的符号表其输出式目标机的指令序列。 本章采用Intel 80x86微处理器作为目标机考虑到通用性主要选用80x86的通用功能如加、减、乘、除法运算等。为了使程序设计简单易实现不对中间代码进行优化也不考虑目标代码是否简洁、寄存器利用是否高效只是依次把每条中间代码根据算符的含义直接翻译为对应的80x86汇编指令。一般情况下假定指令中只使用一个寄存器除非特殊指令需要使用多个寄存器时才使用。80x86使用AX、BX、CX、DX 4个通用寄存器来存放数据。这样的话对每种类型的中间代码可以考虑一般的目标代码生成策略如下表所示 4.3.7 结果分析
【输入1】
xPI4*(5.5-2.2);
y4*E;
?sin(x)cos(y)-tg(x)*ctg(x)/log(x);【输出1】
Program: xPI4*(5.5-2.2);
Semantic Analysis result is: x16.341593
Program: y4*E;
Semantic Analysis result is: y10.840000
Program: ?sin(x)cos(y)-tg(x)*ctg(x)/log(x);
Semantic Analysis result is: -1.104967【输入2】
xPI4*(5.5-2.2);
y4*E;
?sin(x)cos(y)-tg(x)*ctg(a)/log(x);【输出2】
Program: xPI4*(5.5-2.2);
Semantic Analysis result is: x16.341593
Program: y4*E;
Semantic Analysis result is: y10.840000
Program: ?sin(x)cos(y)-tg(x)*ctg(a)/log(x);
Semantic Analysis result is: -0.747021-0.734689*ctg(a)/2.7937144.3.8 代码实现
#include iostream
#include string.h
#include cmathusing namespace std;/* 记号表
Variable - 0
Constant - 1
log - 2
sin - 3
cos - 4
tg - 5
ctg - 6
lg - 7
ln - 8
^ - 9- 10
- - 11
* - 12
/ - 13- 14
? - 15
( - 16
) - 17
, - 18
; - 19
# - 20
*/// 词法分析器
class Lexical {
public:int id;string word; // 词int num; // 记号Lexical *next NULL;// 获取关键字对应记号int GetKeywordNum(string s) {if (s log)return 2;else if (s sin)return 3;else if (s cos)return 4;else if (s tg)return 5;else if (s ctg)return 6;else if (s lg)return 7;else if (s ln)return 8;else return -1;}// 获取运算符对应记号int GetOperatorNum(char ch) {if (ch ^)return 9;else if (ch )return 10;else if (ch -)return 11;else if (ch *)return 12;else if (ch /)return 13;else if (ch )return 14;else return -1;}//获取分隔符对应记号int GetSeparatorNum(char ch) {if (ch ?)return 15;else if (ch ()return 16;else if (ch ))return 17;else if (ch ,)return 18;else if (ch ;)return 19;else if (ch #)return 20;else return -1;}// 状态转移判断下一状态int StateTransfer(int state, char ch) {if (state 0) {if (ch || ch - || ch * || ch / || ch ^ || ch )return 1;else if (ch 0)return 2;else if (ch 1 ch 9)return 3;else if (ch E)return 4;else if (ch P)return 5;else if (ch s)return 9;else if (ch c)return 10;else if (ch t)return 11;else if (ch l)return 12;else if (ch _ || (ch A ch Z) || (ch a ch z))return 8;else if (ch || ch ( || ch ) || ch ; || ch , || ch ?)return 17;else if (ch \\)return 16;elsereturn 18;} else if (state 1) {return 18;} else if (state 2) {if (ch .)return 6;elsereturn 18;} else if (state 3) {if (ch 0 ch 9)return 3;else if (ch .)return 6;elsereturn 18;} else if (state 4) {if (ch _ || (ch A ch Z) || (ch a ch z) || (ch 0 ch 9))return 8;elsereturn 18;} else if (state 5) {if (ch I)return 4;else if (ch _ || (ch A ch Z) || (ch a ch z) || (ch 0 ch 9))return 8;elsereturn 18;} else if (state 6) {if (ch 0)return 6;else if (ch 1 ch 9)return 7;elsereturn 18;} else if (state 7) {if (ch 0)return 6;else if (ch 1 ch 9)return 7;elsereturn 18;} else if (state 8) {if (ch _ || (ch A ch Z) || (ch a ch z) || (ch 0 ch 9))return 8;elsereturn 18;} else if (state 9) {if (ch i)return 13;else if (ch _ || (ch A ch Z) || (ch a ch z ch ! i) ||(ch 0 ch 9))return 8;elsereturn 18;} else if (state 10) {if (ch t)return 11;else if (ch o)return 14;else if (ch _ || (ch A ch Z) || (ch a ch z ch ! t ch ! o) ||(ch 0 ch 9))return 8;elsereturn 18;} else if (state 11) {if (ch g)return 15;else if (ch _ || (ch A ch Z) || (ch a ch z ch ! g) ||(ch 0 ch 9))return 8;elsereturn 18;} else if (state 12) {if (ch o)return 11;else if (ch g || ch n)return 15;else if (ch _ || (ch A ch Z) ||(ch a ch z ch ! o ch ! g ch ! n) || (ch 0 ch 9))return 8;elsereturn 18;} else if (state 13) {if (ch n)return 15;else if (ch _ || (ch A ch Z) || (ch a ch z ch ! n) ||(ch 0 ch 9))return 8;elsereturn 18;} else if (state 14) {if (ch s)return 15;else if (ch _ || (ch A ch Z) || (ch a ch z ch ! s) ||(ch 0 ch 9))return 8;elsereturn 18;} else if (state 15) {if (ch _ || (ch A ch Z) || (ch a ch z) || (ch 0 ch 9))return 8;elsereturn 18;} else if (state 16) {if (ch t || ch n)return 17;elsereturn 18;} else if (state 17) {return 18;} else {return -1;}}// 词法分析int LexicalAnalysis(char arithmetic[]) {int id 1;int len strlen(arithmetic);int preIndex 0;int index 0;int preState 0;int state 0;Lexical *L this;for (index 0; index len; index) {if (index len) state 18;else state StateTransfer(state, arithmetic[index]);if (state 18) {string s;Lexical *p new Lexical;if (preState 1) {//运算符id;p-id id;p-word arithmetic[preIndex];p-num GetOperatorNum(arithmetic[preIndex]);p-next NULL;L-next p;L p;} else if (preState 2 || preState 3 || preState 4 || preState 7) {//常量for (int i preIndex; i index; i)s arithmetic[i];id;p-id id;p-word s;p-num 1;p-next NULL;L-next p;L p;} else if (preState 8) {//变量if ((index - preIndex) 32) {for (int i preIndex; i index; i)cout arithmetic[i];return -1;}for (int i preIndex; i index; i)s arithmetic[i];id;p-id id;p-word s;p-num 0;p-next NULL;L-next p;L p;} else if (preState 15) {//关键字for (int i preIndex; i index; i)s arithmetic[i];id;p-id id;p-word s;p-num GetKeywordNum(s);p-next NULL;L-next p;L p;} else if (preState 17) {//分隔符id;p-id id;p-word arithmetic[preIndex];p-num GetSeparatorNum(arithmetic[preIndex]);p-next NULL;L-next p;L p;} else if (preState 0) {Lexical *p new Lexical;id;p-id id;p-word #;p-num 20;p-next NULL;L-next p;L p;return 1;} else {for (int i preIndex; i index; i)cout arithmetic[i];return -1;}preIndex index;index--;preState 0;state 0;} else if (state -1) {return -1;} else {preState state;}}Lexical *p new Lexical;id;p-id id;p-word #;p-num 20;p-next NULL;L-next p;L p;return 1;}// 输出链表void PrintList() {Lexical *L this-next;while (L) {cout L-word \t L-num \t endl;L L-next;}}
};/* 记号表
Variable - a 0
Constant - b 1
log - c 2
sin - d 3
cos - e 4
tg - f 5
ctg - g 6
lg - h 7
ln - i 8
^ 910
- 11
* 12
/ 1314
? 15
( 16
) 17
, 18
; 19
# 20
S 21
A 22
B 23
C 24
D 25
*/// 语法分析器
char GrammarLeft[21]; // 文法产生式左部
string GrammarRight[21]; // 文法产生式右部
int ShiftReduce[44][26]; // 移进-归约分析表// 初始化文法表
void InitGrammer() {/* 增广文法表(0) S→S(1) S→VariableA;(2) S→?A;(3) A→AB(4) A→A-B(5) A→B(6) B→B*C(7) B→B/C(8) B→C(9) C→log(D,D)(10) C→log(D)(11) C→D^D(12) C→Keyword(D)(13) C→D(14) D→Variable(15) D→-Variable(16) D→Variable(17) D→Constant(18) D→-Constant(19) D→Constant(20) D→(A)*/GrammarLeft[1] S;GrammarLeft[2] S;GrammarLeft[3] A;GrammarLeft[4] A;GrammarLeft[5] A;GrammarLeft[6] B;GrammarLeft[7] B;GrammarLeft[8] B;GrammarLeft[9] C;GrammarLeft[10] C;GrammarLeft[11] C;GrammarLeft[12] C;GrammarLeft[13] C;GrammarLeft[14] D;GrammarLeft[15] D;GrammarLeft[16] D;GrammarLeft[17] D;GrammarLeft[18] D;GrammarLeft[19] D;GrammarLeft[20] D;GrammarRight[1] aA;;GrammarRight[2] ?A;;GrammarRight[3] AB;GrammarRight[4] A-B;GrammarRight[5] B;GrammarRight[6] B*C;GrammarRight[7] B/C;GrammarRight[8] C;GrammarRight[9] c(D,D);GrammarRight[10] c(D);GrammarRight[11] D^D;GrammarRight[12] d(D);GrammarRight[13] D;GrammarRight[14] a;GrammarRight[15] -a;GrammarRight[16] a;GrammarRight[17] b;GrammarRight[18] -b;GrammarRight[19] b;GrammarRight[20] (A);
}// 初始化移进-规约分析表
void InitShiftReduce() {// 正数表示移进负数表示规约99表示accept//0ShiftReduce[0][0] 2;ShiftReduce[0][15] 3;ShiftReduce[0][21] 1;// 1ShiftReduce[1][20] 99;// 2ShiftReduce[2][14] 4;// 3ShiftReduce[3][0] 12;ShiftReduce[3][1] 13;ShiftReduce[3][2] 8;for (int i 3; i 8; i)ShiftReduce[3][i] 14;ShiftReduce[3][10] 13;ShiftReduce[3][11] 14;ShiftReduce[3][16] 15;ShiftReduce[3][22] 5;ShiftReduce[3][23] 6;ShiftReduce[3][24] 7;ShiftReduce[3][25] 9;// 4ShiftReduce[4][0] 12;ShiftReduce[4][1] 13;ShiftReduce[4][2] 8;for (int i 3; i 8; i)ShiftReduce[4][i] 14;ShiftReduce[4][10] 13;ShiftReduce[4][11] 14;ShiftReduce[4][16] 15;ShiftReduce[4][22] 17;ShiftReduce[4][23] 6;ShiftReduce[4][24] 7;ShiftReduce[4][25] 9;// 5ShiftReduce[5][10] 19;ShiftReduce[5][11] 20;ShiftReduce[5][19] 27;// 6ShiftReduce[6][10] -5;ShiftReduce[6][11] -5;ShiftReduce[6][12] 23;ShiftReduce[6][13] 24;ShiftReduce[6][17] -5;ShiftReduce[6][19] -5;// 7for (int i 10; i 13; i)ShiftReduce[7][i] -8;ShiftReduce[7][17] -8;ShiftReduce[7][19] -8;// 8ShiftReduce[8][16] 29;// 9for (int i 10; i 13; i)ShiftReduce[9][i] -13;ShiftReduce[9][9] 35;ShiftReduce[9][17] -13;ShiftReduce[9][19] -13;// 10ShiftReduce[10][0] 37;ShiftReduce[10][1] 38;// 11ShiftReduce[11][0] 39;ShiftReduce[11][1] 40;// 12for (int i 9; i 13; i)ShiftReduce[12][i] -16;for (int i 17; i 19; i)ShiftReduce[12][i] -16;// 13for (int i 9; i 13; i)ShiftReduce[13][i] -19;for (int i 17; i 19; i)ShiftReduce[13][i] -19;// 14ShiftReduce[14][16] 41;// 15ShiftReduce[15][0] 12;ShiftReduce[15][1] 13;ShiftReduce[15][2] 8;for (int i 3; i 8; i)ShiftReduce[15][i] 14;ShiftReduce[15][10] 10;ShiftReduce[15][11] 11;ShiftReduce[15][16] 15;ShiftReduce[15][22] 16;ShiftReduce[15][23] 6;ShiftReduce[15][24] 7;ShiftReduce[15][25] 9;// 16ShiftReduce[16][10] 19;ShiftReduce[16][11] 20;ShiftReduce[16][17] 28;// 17ShiftReduce[17][10] 19;ShiftReduce[17][11] 20;ShiftReduce[17][19] 18;// 18ShiftReduce[18][20] -1;// 19ShiftReduce[19][0] 12;ShiftReduce[19][1] 13;ShiftReduce[19][2] 8;for (int i 3; i 8; i)ShiftReduce[19][i] 14;ShiftReduce[19][10] 10;ShiftReduce[19][11] 11;ShiftReduce[19][16] 15;ShiftReduce[19][23] 22;ShiftReduce[19][24] 7;ShiftReduce[19][25] 9;// 20ShiftReduce[20][0] 12;ShiftReduce[20][1] 13;ShiftReduce[20][2] 8;for (int i 3; i 8; i)ShiftReduce[20][i] 14;ShiftReduce[20][10] 10;ShiftReduce[20][11] 11;ShiftReduce[20][16] 15;ShiftReduce[20][23] 21;ShiftReduce[20][24] 7;ShiftReduce[20][25] 9;// 21ShiftReduce[21][10] -4;ShiftReduce[21][11] -4;ShiftReduce[21][12] 23;ShiftReduce[21][13] 24;ShiftReduce[21][17] -4;ShiftReduce[21][19] -4;// 22ShiftReduce[22][10] -3;ShiftReduce[22][11] -3;ShiftReduce[22][12] 23;ShiftReduce[22][13] 24;ShiftReduce[22][17] -3;ShiftReduce[22][19] -3;// 23ShiftReduce[23][0] 12;ShiftReduce[23][1] 13;ShiftReduce[23][2] 8;for (int i 3; i 8; i)ShiftReduce[23][i] 14;ShiftReduce[23][10] 10;ShiftReduce[23][11] 11;ShiftReduce[23][16] 15;ShiftReduce[23][24] 25;ShiftReduce[23][25] 9;// 24ShiftReduce[24][0] 12;ShiftReduce[24][1] 13;ShiftReduce[24][2] 8;for (int i 3; i 8; i)ShiftReduce[24][i] 14;ShiftReduce[24][10] 10;ShiftReduce[24][11] 11;ShiftReduce[24][16] 15;ShiftReduce[24][24] 26;ShiftReduce[24][25] 9;// 25for (int i 10; i 13; i)ShiftReduce[25][i] -6;ShiftReduce[25][17] -6;ShiftReduce[25][19] -6;// 26for (int i 10; i 13; i)ShiftReduce[26][i] -7;ShiftReduce[26][17] -7;ShiftReduce[26][19] -7;// 27ShiftReduce[27][20] -2;// 28for (int i 9; i 13; i)ShiftReduce[28][i] -20;for (int i 17; i 19; i)ShiftReduce[28][i] -20;// 29ShiftReduce[29][0] 12;ShiftReduce[29][1] 13;ShiftReduce[29][10] 10;ShiftReduce[29][11] 11;ShiftReduce[29][16] 15;ShiftReduce[29][25] 30;// 30ShiftReduce[30][17] 31;ShiftReduce[30][18] 32;// 31for (int i 10; i 13; i)ShiftReduce[31][i] -10;ShiftReduce[31][17] -10;ShiftReduce[31][19] -10;// 32ShiftReduce[32][0] 12;ShiftReduce[32][1] 13;ShiftReduce[32][10] 10;ShiftReduce[32][11] 11;ShiftReduce[32][16] 15;ShiftReduce[32][25] 33;// 33ShiftReduce[33][17] 34;// 34for (int i 10; i 13; i)ShiftReduce[34][i] -9;ShiftReduce[34][17] -9;ShiftReduce[34][19] -9;// 35ShiftReduce[35][0] 12;ShiftReduce[35][1] 13;ShiftReduce[35][10] 10;ShiftReduce[35][11] 11;ShiftReduce[35][16] 15;ShiftReduce[35][25] 36;// 36for (int i 10; i 13; i)ShiftReduce[36][i] -11;ShiftReduce[36][17] -11;ShiftReduce[36][19] -11;// 37for (int i 9; i 13; i)ShiftReduce[37][i] -14;for (int i 17; i 19; i)ShiftReduce[37][i] -14;// 38for (int i 9; i 13; i)ShiftReduce[38][i] -17;for (int i 17; i 19; i)ShiftReduce[38][i] -17;// 39for (int i 9; i 13; i)ShiftReduce[39][i] -15;for (int i 17; i 19; i)ShiftReduce[39][i] -15;// 40for (int i 9; i 13; i)ShiftReduce[40][i] -18;for (int i 17; i 19; i)ShiftReduce[40][i] -18;// 41ShiftReduce[41][0] 12;ShiftReduce[41][1] 13;ShiftReduce[41][10] 10;ShiftReduce[41][11] 11;ShiftReduce[41][16] 15;ShiftReduce[41][25] 42;// 42ShiftReduce[42][17] 43;// 43for (int i 10; i 13; i)ShiftReduce[43][i] -12;ShiftReduce[43][17] -12;ShiftReduce[43][19] -12;
}string variable[100];
double value[100];
int variableIndex 0;class Syntactic {
private:char id; // 符号string name; // 存储变量的名字string val; // 存储常量的值 或 变量的值int childrenNum 0; // 子节点数子节点数为0即为终结符Syntactic **children NULL; // 子节点Syntactic *parent NULL; // 父节点
public:// 将字符转化为整数或小数double TrabsformToInt(string st) {double temp 0;int k 0;int count 1;int flag 0;if (st[0] -) {flag 1;k;} else if (st[0] )k;for (k; k st.length(); k) {if (st[k] .)break;else {temp temp * 10 (st[k] - 0);}}for (k; k st.length(); k) {double t (st[k] - 0);for (int i 0; i count; i)t / 10;temp t;count;}if (flag 1)temp -1 * temp;return temp;}// 获取字符对应记号int GetCharNum(char ch) {if (ch a)return 0;else if (ch b)return 1;else if (ch c)return 2;else if (ch d)return 3;else if (ch e)return 4;else if (ch f)return 5;else if (ch g)return 6;else if (ch h)return 7;else if (ch i)return 8;else if (ch ^)return 9;else if (ch )return 10;else if (ch -)return 11;else if (ch *)return 12;else if (ch /)return 13;else if (ch )return 14;else if (ch ?)return 15;else if (ch ()return 16;else if (ch ))return 17;else if (ch ,)return 18;else if (ch ;)return 19;else if (ch #)return 20;else if (ch S)return 21;else if (ch A)return 22;else if (ch B)return 23;else if (ch C)return 24;else if (ch D)return 25;else return -1;}// 查找变量是否存在值int SearchVariable(string st) {for (int i 0; i 100; i)if (variable[i] st)return i;return -1;}// 归约计算void calculate(Syntactic *Sy, int calculate_id) {if (calculate_id 1) {if (Sy-children[2]-val ! ) {Sy-id a;Sy-name Sy-children[0]-name;Sy-val Sy-children[2]-val;variable[variableIndex] Sy-name;value[variableIndex] TrabsformToInt(Sy-val);Sy-childrenNum 0;Sy-children NULL;} else {Sy-id a;Sy-name Sy-children[0]-name;Sy-childrenNum Sy-children[2]-childrenNum;Sy-children Sy-children[2]-children;}variableIndex;} else if (calculate_id 2) {Sy-children[0] Sy-children[1];Sy-childrenNum 1;}// A-ABelse if (calculate_id 3 Sy-children[0]-val ! Sy-children[2]-val ! ) {Sy-id b;Sy-val std::to_string(TrabsformToInt(Sy-children[0]-val) TrabsformToInt(Sy-children[2]-val));Sy-childrenNum 0;Sy-children NULL;}// A-A-Belse if (calculate_id 4 Sy-children[0]-val ! Sy-children[2]-val ! ) {Sy-id b;Sy-val std::to_string(TrabsformToInt(Sy-children[0]-val) - TrabsformToInt(Sy-children[2]-val));Sy-childrenNum 0;Sy-children NULL;}// A-B 、 B-C 、 C-Delse if (calculate_id 5 || calculate_id 8 || calculate_id 13) {if (Sy-children[0]-val ! ) {Sy-id Sy-children[0]-id;Sy-name Sy-children[0]-name;Sy-val Sy-children[0]-val;Sy-childrenNum 0;Sy-children NULL;} else {Sy-id Sy-children[0]-id;Sy-name Sy-children[0]-name;Sy-childrenNum Sy-children[0]-childrenNum;Sy-children Sy-children[0]-children;}}// B-B*Celse if (calculate_id 6 Sy-children[0]-val ! Sy-children[2]-val ! ) {Sy-id b;Sy-val std::to_string(TrabsformToInt(Sy-children[0]-val) * TrabsformToInt(Sy-children[2]-val));Sy-childrenNum 0;Sy-children NULL;}// B-B/Celse if (calculate_id 7 Sy-children[0]-val ! Sy-children[2]-val ! ) {Sy-id b;Sy-val std::to_string(TrabsformToInt(Sy-children[0]-val) / TrabsformToInt(Sy-children[2]-val));Sy-childrenNum 0;Sy-children NULL;}// C-log(D,D)else if (calculate_id 9 Sy-children[2]-val ! Sy-children[4]-val ! ) {Sy-id b;Sy-val std::to_string(log(TrabsformToInt(Sy-children[4]-val)) / log(TrabsformToInt(Sy-children[2]-val)));Sy-childrenNum 0;Sy-children NULL;}// C-log(D)else if (calculate_id 10 Sy-children[2]-val ! ) {Sy-id b;Sy-val std::to_string(log(TrabsformToInt(Sy-children[2]-val)));Sy-childrenNum 0;Sy-children NULL;}// C-D^Delse if (calculate_id 11 Sy-children[0]-val ! Sy-children[2]-val ! ) {Sy-id b;Sy-val std::to_string(pow(TrabsformToInt(Sy-children[0]-val), TrabsformToInt(Sy-children[2]-val)));Sy-childrenNum 0;Sy-children NULL;}// C-Keyword(D)else if (calculate_id 12 Sy-children[2]-val ! ) {Sy-id b;double t;// sinif (Sy-children[0]-id d)t sin(TrabsformToInt(Sy-children[2]-val));// coselse if (Sy-children[0]-id e)t cos(TrabsformToInt(Sy-children[2]-val));// tgelse if (Sy-children[0]-id f)t tan(TrabsformToInt(Sy-children[2]-val));// ctgelse if (Sy-children[0]-id g)t 1 / tan(TrabsformToInt(Sy-children[2]-val));// lgelse if (Sy-children[0]-id h)t log10(TrabsformToInt(Sy-children[2]-val));// lnelse if (Sy-children[0]-id h)t log(TrabsformToInt(Sy-children[2]-val));Sy-val std::to_string(t);Sy-childrenNum 0;Sy-children NULL;}// D-Variableelse if (calculate_id 14) {if (Sy-children[1]-val ! ) {Sy-id b;Sy-val Sy-children[1]-val;Sy-childrenNum 0;Sy-children NULL;} else {Sy-id a;Sy-name Sy-children[1]-name;Sy-childrenNum 0;Sy-children NULL;}}// D--Variableelse if (calculate_id 15 Sy-children[1]-val ! ) {Sy-id b;Sy-val - Sy-children[1]-val;Sy-childrenNum 0;Sy-children NULL;}// D-Variableelse if (calculate_id 16) {if (Sy-children[0]-val ! ) {Sy-id b;Sy-val Sy-children[0]-val;Sy-childrenNum 0;Sy-children NULL;} else {Sy-id a;Sy-name Sy-children[0]-name;Sy-childrenNum 0;Sy-children NULL;}}// D-Constantelse if (calculate_id 17 Sy-children[1]-val ! ) {Sy-id b;Sy-val Sy-children[1]-val;Sy-childrenNum 0;Sy-children NULL;}// D--Constantelse if (calculate_id 18 Sy-children[1]-val ! ) {Sy-id b;Sy-val - Sy-children[1]-val;Sy-childrenNum 0;Sy-children NULL;}// D-Constantelse if (calculate_id 19 Sy-children[0]-val ! ) {Sy-id b;Sy-val Sy-children[0]-val;Sy-childrenNum 0;Sy-children NULL;}// D-(A)else if (calculate_id 20 Sy-children[1]-val ! ) {Sy-id b;Sy-val Sy-children[1]-val;Sy-childrenNum 0;Sy-children NULL;}}// 语法分析-自下而上int SyntacticAnalysis_SLR(Lexical *Le) {Lexical *head Le-next;Le Le-next;Syntactic **SyTemp new Syntactic *[100];int tempIndex 0;Syntactic *Sy this;string stack[100];int index 0;stack[0] #;stack[1] 0;index 2;while (1) {int row TrabsformToInt(stack[index - 1]);int col Le-num;int action ShiftReduce[row][col];if (action 99) {Sy-id SyTemp[0]-id;Sy-name SyTemp[0]-name;Sy-val SyTemp[0]-val;Sy-childrenNum SyTemp[0]-childrenNum;Sy-children SyTemp[0]-children;return 1;}// 移进else if (action 0) {string s;s to_string(action);if (Le-num 8)stack[index] (char) (int(a) Le-num);elsestack[index] Le-word;stack[index 1] s;Syntactic *q new Syntactic;q-id stack[index][0];if (Le-num 0) {q-name Le-word;int t SearchVariable(q-name);if (t ! -1)q-val std::to_string(value[t]);} else if (Le-num 1) {if (Le-word PI)q-val 3.1415926;else if (Le-word E)q-val 2.71;else q-val Le-word;}SyTemp[tempIndex] q;tempIndex;index 2;Le Le-next;}// 归约else if (action 0) {int reduceNum -action;string left;left GrammarLeft[-action];string right GrammarRight[-action];index - right.length() * 2;if (-action 12)right[0] stack[index][0];stack[index] left;row TrabsformToInt(stack[index - 1]);col GetCharNum(GrammarLeft[-action]);action ShiftReduce[row][col];string s;s to_string(action);stack[index 1] s;Syntactic *q new Syntactic;q-id stack[index][0];q-children new Syntactic *[right.length()];q-childrenNum right.length();for (int k right.length() - 1; k 0; k--) {SyTemp[tempIndex - 1]-parent q;q-children[k] SyTemp[tempIndex - 1];tempIndex--;}SyTemp[tempIndex] q;tempIndex;calculate(q, reduceNum);index 2;}}return 1;}// 先序遍历语法分析树void DFS(Syntactic *Sy, int tier) {if (Sy-id a)cout Sy-name;else if (Sy-id b)cout Sy-val;else if (Sy-id c)cout log;else if (Sy-id d)cout sin;else if (Sy-id e)cout cos;else if (Sy-id f)cout tg;else if (Sy-id g)cout ctg;else if (Sy-id h)cout lg;else if (Sy-id i)cout ln;else if (Sy-id S || (Sy-id A Sy-id D));else cout Sy-id;if (tier 0 Sy-id a)cout ;for (int i 0; i Sy-childrenNum; i)DFS(Sy-children[i], tier 1);}// 输出语法分析树void PrintTree() {Syntactic *Sy this;if (Sy-childrenNum 0)cout Sy-name Sy-val;elseDFS(Sy, 0);cout endl;}
};int main() {InitGrammer();InitShiftReduce();char arithmetic[3][100] {xPI4*(5.5-2.2);,y4*E;,?sin(x)cos(y)-tg(x)*ctg(y)/log(x);};for (int i 0; i 3; i) {// 词法分析部分Lexical *Le new Lexical;cout Program: arithmetic[i] endl;Le-LexicalAnalysis(arithmetic[i]);// 语法分析部分Syntactic Sy;Sy.SyntacticAnalysis_SLR(Le);cout Syntax Analysis result is: ;Sy.PrintTree();cout endl;}return 0;
}
参考文献
刘铭. 编译原理[M]. 北京电子工业出版社2018.黄贤英. 编译原理及实践教程[M]. 北京清华大学出版社2019.Keith Cooper. 编译器设计[M]. 北京人民邮电出版社2012.
作者Sylvan Ding 转载请注明文章出处 ❤️
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/91769.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!