编译原理课程实践——实现一个初等函数运算语言的解释器或编译器

编译原理课程实践——实现具有初等函数运算语言的解释器或编译器

作者: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 实验内容及要求

根据下面的要求设计初等函数运算语言的词法模式,并用正则式表达出来。

  1. 初等函数运算语言的常量为实数类型,其定义方式为实数的最一般书写方式,如:123.321。具体要求:不支持整数部分大于0时首数字为0;不支持小数点后结尾为0;不支持科学记数法;不支持仅为整数时有小数点。
  2. 初等函数运算语言的变量采用与C语言的标识符定义一样的方式:首字符为字母或下划线;其他的为字母、数字及下划线的混合串;区分大小写;变量长度不超过32个字符。
  3. 初等函数运算语言需要处理的函数仅为表一中所列举的内容。
  4. 初等函数运算语言支持四则运算,其计算的符号与C语言相同,为:±*/。
  5. 初等函数运算语言的合法的分隔符包括:空格、制表符、分行符圆括号(左、右)、分号。其中空格、制表符、分行符可以出现在任何两个不同的单词中间;圆括号(左、右)用于表达式中,用于改变运算的优先级,以及标识函数的参数;分号用于标识一个语句的结束。
  6. 初等函数运算语言支持的常量还包括:PI,E。

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 = (((19)(09))0)((.(09)(19))ϵ)

其中,

(1∣…∣9)(0∣…∣9)∗(1|\ldots|9)(0|\ldots|9)\ast(19)(09) 满足“不支持整数部分大于0时首数字为0”,

(.(0∣…∣9)∗(1∣…∣9))(.(0|\ldots|9)\ast(1|\ldots|9))(.(09)(19)) 满足“不支持小数点后结尾为0”,

((.(0∣…∣9)∗(1∣…∣9))∣ϵ)((.(0|\ldots|9)\ast(1|\ldots|9))|\epsilon)((.(09)(19))ϵ) 满足“不支持仅为整数时有小数点”,

(((1∣…∣9)(0∣…∣9)∗)∣0)(((1|\ldots|9)(0|\ldots|9)\ast)|0)(((19)(09))0) 考虑了整数部分为0时的情况。

具体定义过程是:

zero=0zero\ =\ 0zero = 0

nonzero=1∣...∣9nonzero\ =\ 1\ |\ ...\ |\ 9nonzero = 1  ...  9

digit=zero∣nonzerodigit\ =\ zero\ |\ nonzerodigit = zero  nonzero

digits=digitdigit∗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)  ϵ

Number=integerPartoptionalFractionNumber\ =\ integerPart\ optionalFractionNumber = integerPart optionalFraction

在这里插入图片描述

2.3.2 设计标识符的正规式并构造NFA

运算语言支持的函数(需求3)、常量PI和E(需求6)可作为一类特殊的标识符来处理,不再专设对应的转换图,用户不得使用它们作为自己定义的标识符,但需要把它们预先安排在一个表格中(称为关键字表)。当利用DFA识别出一个“标识符”时,就去查关键字表,以确定标识符是否为一常量或函数。

设计满足上述要求2的标识符的正规式:

digit=0∣...∣9digit\ =\ 0|...|9digit = 0...9

letter=a∣...∣z∣A∣...∣Z∣_letter\ =\ a|...|z|A|...|Z|\_letter = a...zA...Z_

id=letter(letter∣digit)∗id\ =\ letter(letter|digit)\astid = letter(letterdigit)

在这里插入图片描述

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′S'S,然后从S′S'S出发,经过对输入符号 a∈Σa\in\SigmaaΣ 的状态转移所能到达的状态的 ϵ−CLOSURE\epsilon-CLOSUREϵCLOSURE 所组成的集合作为M的新状态,如此重复,直到不再有新的状态出现为止。构造Q′Q'Qf′f'f的算法描述如下:

在这里插入图片描述

使用上述子集法求NFA N的等价DFA M:

(注:q0,q1,…,qn∈Q′q0,q1,\ldots,qn\in Q^\primeq0,q1,,qnQ,简记为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,.,+,,,/,,(,),;,?,=}
S=0S = 0S=0
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用来识别标识符(包括常量、函数名),终态4~13用来识别符号(包括运算符、界符)。更具体地,状态1识别非零整数,状态2识别整数零,状态15识别浮点数。

在这里插入图片描述

2.3.7 编写词法分析程序

根据实验要求,列写出该语言的所有单词符号:

在这里插入图片描述

规定若函数名、标识符和常数之间没有确定的运算符或界符作间隔,则必须至少用一个空白符作间隔,即此时的空白符是有意义的。接下来根据状态转换图构造出词法分析器,在此引进词法分析程序所用的全局变量和需要调用的函数如下:

  1. ch字符变量,存放当前读进的源程序字符;
  2. token字符数组,存放构成单词符号的字符串;
  3. MAX_TOKEN_LEN,整型常量,存放最大token长度(按题意设置为32);
  4. TokenCode,枚举类型,列出所有符号及其种别码;
  5. getch()读字符函数,每调用一次从输入缓冲区中读进源程序的下一个字符放在ch中,并把读字符指针指向下一个字符;
  6. getbc()函数,每次调用时,检查ch中的字符是否为空白字符(包括空格、换行符和制表符),若是空白字符,则反复调用getch(),直至ch中读入一个非空白字符为止;
  7. concat()函数,每次调用把当前ch中的字符与token中的字符串连接,同时检查token是否超过MAX_TOKEN_LEN,若超过则报错;
  8. letter(ch)、digit(ch)、zero(ch)、nonzero(ch)布尔函数,分别判定ch中的字符是否为字母和数字(以及数字的类型),从而给出true或false值;
  9. reserve()整型函数,对token中的字符串查关键字表,若它是一个关键字(函数名或常量),则返回它的编码,否则返回标识符的种别码11。关键字表用generateKeyList()来生成,关键字表存放在keyList中;
  10. retract()函数,读字符指针回退一个字符;
  11. return()函数,收集并携带必要的信息返回调用程序,即返回语法分析程序;
  12. stof()函数,将token中的数字串转换成浮点数;
  13. 词法分析程序scan()返回的单词符号为二元式:(单词种别,单词自身值),在程序中以Word类表征。仅当单词种别是标识符和数字时,单词自身值才存在,并以字符串形式保存;
  14. saveWord(Word)将scan()返回的结果保存在vector容器中。
//
// Created by Sylvan Ding on 2022/5/10.
//#include <iostream>
#include <cstring>
#include <vector>
#include <map>using 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;vector<Word> words;string program;map<string, 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(pair<string, TokenCode>("sin", TC_SIN));keyList.insert(pair<string, TokenCode>("cos", TC_COS));keyList.insert(pair<string, TokenCode>("tg", TC_TG));keyList.insert(pair<string, TokenCode>("ctg", TC_CTG));keyList.insert(pair<string, TokenCode>("log", TC_LOG));keyList.insert(pair<string, TokenCode>("lg", TC_LG));keyList.insert(pair<string, TokenCode>("ln", TC_LN));keyList.insert(pair<string, TokenCode>("PI", TC_PI));keyList.insert(pair<string, 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))+(7+z)^2;";LexicalAnalyze lexAna(program);return 0;
}

2.3.8 结果分析

在这里插入图片描述

在这里插入图片描述

第3章 语法分析器设计

3.1 实验目的

1、为初等函数运算语言构造LL(1)语法分析器;
2、掌握LL(1)语法分析器的方法,加深对自上而下语法分析原理的理解;
3、掌握设计、编制并调试LL(1)语法分析程序的思想和方法。

3.2 实验内容及要求

1、根据初等函数运算语言运算法则,将语法模式用上下文无关文法表达;

2、注意运算的优先性,避免产生二义性文法;将上述文法改写为LL(1)文法;

3、根据LL(1)文法给出预测分析表;

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\alphaAAα 的左线性产生式(或是存在推导在这里插入图片描述),分析过程又使用最左推导,在递归的过程中就会陷入无限循环。对含有直接左递归的文法规则进行等价变换以消除左递归,方法如下:

在这里插入图片描述

【算术表达式提取公共左因子】

在这里插入图片描述

在这里插入图片描述

3.3.3 赋值表达式的文法描述

相较于算术表达式,赋值表达式的优先级更低,其含义是把赋值号右边的表达式赋值给左边的一个标识符,赋值表达式的文法如下

<assign>→<id>=<expr><assign>\rightarrow<id>=<expr><assign><id>=<expr>

为其中非终结符重新命名,得到如下文法:G3

D→id=ED\rightarrow id=EDid=E

3.3.4 语言程序的文法描述

根据“实验要求的输入输出示例表”,“程序”用来定义一个合法的语言结构,由赋值语句和运算语句组成,赋值语句包含多条赋值表达式,而运算语句则只能包含一条运算语句,其文法定义如下:G4

<program>→<assignment><calculate><program>\rightarrow<assignment><calculate><program><assignment><calculate>

<assignment>→D;<assignment>∣ϵ<assignment>\rightarrow D;<assignment>|\epsilon<assignment>D;<assignment>ϵ

<calculate>→?E;<calculate>\rightarrow?E;<calculate>?E;

程序语言分区思想的示意图如下:

在这里插入图片描述

对文法G4进行重新命名并化简后,得到文法G5:

P→S?E;P\rightarrow S?E;PS?E;

S→D;S∣ϵS\rightarrow D;S|\epsilonSD;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\{\#\}AVN,aVT{#}. 分析表元素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)aSelect(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串是否符合文法要求,输出是语法树。递归下降语法分析器分析程序的接口如下图所示:

在这里插入图片描述

构造递归下降分析程序时,每个函数名是相应的非终结符,函数体是根据规则右部符号串的结构编写的,这组函数以相互递归的方式进行调用。当面对多个候选式时,将根据下一个读入的输入符号和上述预测分析表采用合适的候选式。

语法分析程序所用的全局变量和需要调用的函数如下:

  1. Token,Word类实例,语法分析器读入的一个单词符号;
  2. Node,语法树结点的数据结构,包含成员变量token、X和children. token记录叶子结点对应的终结符信息,X记录递归下降过程中非叶子结点对应的非终结符类型,children记录该结点的孩子结点指针(通过children是否为空判断是否是叶子结点);
  3. root,指向语法树根结点的指针;
  4. DFS(Node*),深度优先搜索函数,遍历语法树并打印;
  5. match(TokenCode,Node*),当前Token是否匹配终结符,若匹配则创建叶子结点,反之,提示Token与栈顶终结符不匹配;
  6. P(),起始非终结符递归函数,递归分析的入口函数,不参与后续递归,创建语法树根结点;
  7. VN,枚举型,表示非终结符的类型;
  8. getTK(),从输入的token串中取出一个单词符号,指针(迭代器)后移一个单词;
  9. isk(),判断Token是否是终结符k(sin,cos,tg,ctg,lg,ln);
  10. isID(),判断Token是否是标识符(包括已定义常量,如E、PI等);
  11. CheckInput(TokenCode *First,TokenCode *Follow),检查Token是否在First集中,若Token不在First集中,则取调用getTK()取下一个Token,直到新的Token在First集或Follow集(包含#)中,以#作为结束符号;
  12. syntaxError(Word&, ErrorType),报告语法错误,参数为错误发生的token信息和错误的类型。因为需要指出错误发生的位置,所以在Word类中添加一个整型变量,记录token所在行数。ErrorType错误类型有两种,分别是终结符与当前输入符号不匹配和非终结符与当前输入符号不匹配;
  13. 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】

a=3;
?1/sin(log(2,123.321))+log(7+z)^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】

a=0.1;
b=3;
?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】

a=4;?a++1-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 表达式 a=3;?1/sin(log(2,123.321))+log(7+z)^PI; 的语法树

在这里插入图片描述

图 3-4 表达式 a=0.1;b=3;?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.	    vector<Node *> 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.	    vector<Word>::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 = "a=4;?a+1-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({SS}) ,得到初态项目集规范族;
(2)对初态项目集或其他已构造的项目集,应用状态转移函数GO(I,X),求出新的项目集(后继状态);
(3)重复步骤(2)直到不出现新的项目集为止;

整个构造LR(0)项目集规范族和识别活前缀的DFA的过程可以描述为算法3.10.

在这里插入图片描述

在这里插入图片描述


⚠️ 接下来的内容皆为移花接木,内容与本文完全不符,但解题流程正确!

⚠️ 接下来的内容皆为移花接木,内容与本文完全不符,但解题流程正确!

⚠️ 接下来的内容皆为移花接木,内容与本文完全不符,但解题流程正确!

✅ 若要完成此部分,请参考刘坚老师的《编译原理基础》!

构造LR(0)项目集规范族和识别活前缀的DFA:

I0={0,5,9,10}
I1=GO(I0,S)={1}
I2=GO(I0,D)={6}
I3=GO(I0,id')={11}
I4=GO(I1,?)={2,14,18,22,24,28,32,34,38,40,44,46,48,50,55}
I5=GO(I2,;)={5,7,10}
I6=GO(I3,=)={12,14,18,22,24,28,32,34,38,40,44,46,48,50,55}
I7=GO(I4,E)={3,15,19}
I8=GO(I4,A)={23,25,29}
I9=GO(I4,B)={33,35}
I10=GO(I4,F)={39}
I11=GO(I4,()={41}
I12=GO(I4,C)={45}
I13=GO(I4,id)={47}
I14=GO(I4,i)={49}
I15=GO(I4,k')={51}
I16=GO(I4,log)={51,56}
I2=GO(I5,D)={6}
I17=GO(I5,S)={8}
I3=GO(I5,id')={11}
I18=GO(I6,E)={13,15,19}
I8=GO(I6,A)={23,25,29}
I9=GO(I6,B)={33,35}
I10=GO(I6,F)={39}
I11=GO(I6,()={41}
I12=GO(I6,C)={45}
I13=GO(I6,id)={47}
I14=GO(I6,i)={49}
I15=GO(I6,k')={51}
I16=GO(I6,log)={51,56}
I19=GO(I7,;)={4}
I20=GO(I7,+)={16,24,28,32,34,38,40,44,46,48,50,55}
I21=GO(I7,-)={20,24,28,32,34,38,40,44,46,48,50,55}
I22=GO(I8,*)={26,34,38,40,44,46,48,50,55}
I23=GO(I8,/)={30,34,38,40,44,46,48,50,55}
I24=GO(I9,^)={36,40,44,46,48,50,55}
I25=GO(I11,E)={42}
I26=GO(I15,()={14,18,22,24,28,32,34,38,40,44,46,48,50,52,55}
I26=GO(I16,()={14,18,22,24,28,32,34,38,40,44,46,48,50,52,55,57}
I20=GO(I18,+)={16,24,28,32,34,38,40,44,46,48,50,55}
I21=GO(I18,-)={20,24,28,32,34,38,40,44,46,48,50,55}
I27=GO(I20,A)={17,25,29}
I9=GO(I20,B)={33,35}
I10=GO(I20,F)={39}
I28=GO(I20,()={14,18,22,24,28,32,34,38,40,41,44,46,48,50,55}
I12=GO(I20,C)={45}
I13=GO(I20,id)={47}
I14=GO(I20,i)={49}
I15=GO(I20,k')={51}
I16=GO(I20,k)={51,56}
I29=GO(I21,A)={21,25,29}
I9=GO(I21,B)={33,35}
I10=GO(I21,F)={39}
I28=GO(I21,()={14,18,22,24,28,32,34,38,40,41,44,46,48,50,55}
I12=GO(I21,C)={45}
I13=GO(I21,id)={47}
I14=GO(I21,i)={49}
I15=GO(I21,k')={51}
I16=GO(I21,k)={51,56}
I30=GO(I22,B)={27,35}
I10=GO(I22,F)={39}
I28=GO(I22,()={14,18,22,24,28,32,34,38,40,41,44,46,48,50,55}
I12=GO(I22,C)={45}
I13=GO(I22,id)={47}
I14=GO(I22,i)={49}
I15=GO(I22,k')={51}
I16=GO(I22,k)={51,56}
I31=GO(I23,B)={31,35}
I10=GO(I23,F)={39}
I28=GO(I23,()={14,18,22,24,28,32,34,38,40,41,44,46,48,50,55}
I12=GO(I23,C)={45}
I13=GO(I23,id)={47}
I14=GO(I23,i)={49}
I15=GO(I23,k')={51}
I16=GO(I23,k)={51,56}
I32=GO(I24,F)={37}
I28=GO(I24,()={14,18,22,24,28,32,34,38,40,41,44,46,48,50,55}
I12=GO(I24,C)={45}
I13=GO(I24,id)={47}
I14=GO(I24,i)={49}
I15=GO(I24,k')={51}
I16=GO(I24,k)={51,56}
I33=GO(I25,))={43}
I34=GO(I26,E)={15,19,53,58}
I22=GO(I27,*)={26,34,38,40,44,46,48,50,55}
I23=GO(I27,/)={30,34,38,40,44,46,48,50,55}
I35=GO(I28,E)={15,19,42}
I36=GO(I28,A)={25,29}
I9=GO(I28,B)={33,35}
I10=GO(I28,F)={39}
I12=GO(I28,C)={45}
I13=GO(I28,id)={47}
I14=GO(I28,i)={49}
I15=GO(I28,k')={51}
I16=GO(I28,k)={51,56}
I22=GO(I29,*)={26,34,38,40,44,46,48,50,55}
I23=GO(I29,/)={30,34,38,40,44,46,48,50,55}
I37=GO(I30,^)={36,40,44,46,48,50,55}
I37=GO(I31,^)={36,40,44,46,48,50,55}
I20=GO(I34,+)={16,24,28,32,34,38,40,44,46,48,50,55}
I21=GO(I34,-)={20,24,28,32,34,38,40,44,46,48,50,55}
I38=GO(I34,))={54}
I39=GO(I34,,)={14,18,22,24,28,34,38,40,44,46,48,50,55,59}
I20=GO(I35,+)={16,24,28,32,34,38,40,44,46,48,50,55}
I21=GO(I35,-)={20,24,28,32,34,38,40,44,46,48,50,55}
I33=GO(I35,))={43}
I22=GO(I36,*)={26,34,38,40,44,46,48,50,55}
I23=GO(I36,/)={30,34,38,40,44,46,48,50,55}
I40=GO(I37,F)={37}
I28=GO(I37,()={14,18,22,24,28,32,34,38,40,41,44,46,48,50,55}
I12=GO(I37,C)={45}
I13=GO(I37,id)={47}
I14=GO(I37,i)={49}
I15=GO(I37,k')={51}
I16=GO(I37,k)={51,56}
I41=GO(I39,E)={15,19,60}
I36=GO(I39,A)={23,25,29}
I9=GO(I39,B)={33,35}
I10=GO(I39,F)={39}
I12=GO(I39,C)={45}
I13=GO(I39,id)={47}
I14=GO(I39,i)={49}
I15=GO(I39,k')={51}
I16=GO(I39,k)={51,56}
I20=GO(I41,+)={16,24,28,32,34,38,40,44,46,48,50,55}
I21=GO(I41,-)={20,24,28,32,34,38,40,44,46,48,50,55}
I42=GO(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是运算符,arg1,arg2分别是第一和第二个运算对象。当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::map<std::string, float>charT;

在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】

x=PI+4*(5.5-2.2);
y=4*E;
?sin(x)+cos(y)-tg(x)*ctg(x)/log(x);

【输出1】

Program: x=PI+4*(5.5-2.2);
Semantic Analysis result is: x=16.341593
Program: y=4*E;
Semantic Analysis result is: y=10.840000
Program: ?sin(x)+cos(y)-tg(x)*ctg(x)/log(x);
Semantic Analysis result is: -1.104967

【输入2】

x=PI+4*(5.5-2.2);
y=4*E;
?sin(x)+cos(y)-tg(x)*ctg(a)/log(x);

【输出2】

Program: x=PI+4*(5.5-2.2);
Semantic Analysis result is: x=16.341593
Program: y=4*E;
Semantic Analysis result is: y=10.840000
Program: ?sin(x)+cos(y)-tg(x)*ctg(a)/log(x);
Semantic Analysis result is: -0.747021-0.734689*ctg(a)/2.793714

4.3.8 代码实现

#include <iostream>
#include <string.h>
#include <cmath>using 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
^               9
+               10
-               11
*               12
/               13
=               14
?               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→Variable=A;(2) S→?A;(3) A→A+B(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] = "a=A;";GrammarRight[2] = "?A;";GrammarRight[3] = "A+B";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->A+Belse 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] = {"x=PI+4*(5.5-2.2);","y=4*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;
} 

参考文献

  1. 刘铭. 编译原理[M]. 北京:电子工业出版社,2018.
  2. 黄贤英. 编译原理及实践教程[M]. 北京:清华大学出版社,2019.
  3. Keith Cooper. 编译器设计[M]. 北京:人民邮电出版社,2012.

作者:Sylvan Ding |转载请注明文章出处! ❤️

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/484564.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构开发(3):线性表的顺序存储结构

0.目录 1.线性表的本质和操作 2.线性表的顺序存储结构 3.顺序存储结构的抽象实现和具体实现 3.1 SeqList3.2 StaticList 和 DynamicList4.顺序存储线性表的分析 4.1 效率分析4.2 功能分析5.小结 1.线性表的本质和操作 线性表 ( List ) 的表现形式&#xff1a; 零个或多个数据元…

小目标检测的一些问题,思路和方案

来源&#xff1a;机器学习研究组订阅机器学习正越来越多地进入我们的日常生活。从个人服务的广告和电影推荐&#xff0c;到自动驾驶汽车和自动送餐服务。几乎所有的现代自动化机器都能“看”世界&#xff0c;但跟我们不一样。为了像我们人类一样看到和识别每个物体&#xff0c;…

python-条件语句

#条件、循环和其他语句 #print:可以打印多个表达式&#xff0c;表达式之间用逗号隔开 print(a,"b",False)#参数并不构成一个元组 模块导入:import x :导入模块xfrom x import func &#xff1a;导入模块x的函数funcfrom x import func1,func2,... 导入模块…

一文读懂全球半导体市场

来源&#xff1a;深城物联作者&#xff1a;孙卓异&#xff0c;供职于赛迪顾问集成电路产业研究中心 半导体是当今信息技术产业高速发展的基础和原动力&#xff0c;已经高度渗透并融合到了经济、社会发展的各个领域&#xff0c;其技术水平和发展规模已经成为衡量一个国家产业竞争…

如何写好一份技术简历?

写简历的基本目的和策略 大部分情况下&#xff0c;写简历是找工作的第一步&#xff0c;考虑到第二步就是面试&#xff0c;那么简历就是敲门砖&#xff0c;为了让企业认识到你的价值&#xff0c;必须把自己的真实水平描述出来&#xff0c;展现出你有能力应对这份工作。甚至要体现…

这是我看过最全的工业机器人知识介绍 !

来源&#xff1a;产业智能官编者按工业机器人广泛使用在产业制造上&#xff0c;汽车制造、电器、食品等&#xff0c;能替代反复机器式操纵工作&#xff0c;是靠本身动力和控制才能来实现种种功用的一种机器。它能够承受人类指挥&#xff0c;也能够按照事先编排的程序运转。今天…

表白网站|程序猿的爱情记录网站模版|情侣日记网页

程序猿的爱情记录网站模版&#xff5c;情侣日记网页设计 介绍 我为我的女朋友制作了这个主页&#xff0c;目的是记录一些值得纪念的时刻。 如果需要&#xff0c;您可以复制和修改此模板作为送给女朋友或妻子的礼物。 Demo Click here to review the website! ❤️ https://…

python-字符串方法

#find方法&#xff1a;查找子串&#xff0c;返回子串所在位置的最左端索引&#xff0c;如果没有找到则返回-1 s"agsa" print(s.find("gs")) print(s.find("agsaa")) #可以指定匹配的起始点和结束点参数,包含第一个索引&#xff0c;不包含第二个索…

智慧食堂数据分析系统

智慧食堂数据分析系统&#xff5c;大数据分析&#xff5c;数据可视化 Demo Repo&#xff1a;https://github.com/sylvanding/AI-Restaurant-Data-Analysis项目演示&#xff08;模拟真实运行环境&#xff09;&#xff1a;http://analysis.sylvanding.online数据展示静态页面&am…

鸿蒙系统全面解析,诞生背景、技术细节生态圈一文看懂

编辑&#xff1a;智东西内参华为6月2日正式发布的鸿蒙系统无疑占据了最近热点话题的C位&#xff0c;虽然不全是赞美的声音&#xff0c;但这种努力打破美国垄断&#xff0c;挑战谷歌、苹果在移动操作系统上垄断地位的尝试必将成为中国科技史上的里程碑事件。本期的智能内参&…

python-字典

字典映射&#xff1a;通过名字来引用值&#xff1b;字典是python中唯一内建的映射类型&#xff1b;1)创建字典&#xff1a;字典有键-值对(项)组成&#xff0c;键和值之间通过冒号(:)隔开&#xff0c;项之间通过逗号(,)分割&#xff0c;整个字典由大括号括起来&#xff1b;空字典…

2021十大人工智能趋势

来源&#xff1a;雷锋网6月5日&#xff0c;以“交叉、融合、相生、共赢”为主题的2021全球人工智能技术大会&#xff08;GAITC 2021&#xff09;在杭州举行。会上&#xff0c;腾讯优图联合厦门大学人工智能研究院共同发布《2021十大人工智能趋势》&#xff08;以下简称“趋势报…

通过CTY、VTY、TTY访问网络设备[计网实践Cisco Packet Tracer]

实验一&#xff1a;接入网络设备 学习目标 CTY访问网络设备VTY访问网络设备TTY访问网络设备WEB访问网络设备 实验环境 Cisco Packet Tracer 6.0 原创文章&#xff0c;转载请注明出处&#xff1a;©️Sylvan Ding ❤️ 实验内容 CTY访问设备 CTY是指通过Console接口访…

细数二十世纪最伟大的10大算法

来源&#xff1a;深度学习于机器视觉编辑&#xff1a;nhyilin一、1946 蒙特卡洛方法[1946: John von Neumann, Stan Ulam, and Nick Metropolis, all at the Los Alamos Scientific Laboratory, cook up the Metropolis algorithm, also known as the Monte Carlo method.]1946…

python-元组

不可变序列&#xff1a;元组&#xff0c;字符串元组用两个圆括号()来表示;用逗号分隔一些值&#xff0c;则自动创建了元组a(1,2,3) print(a) print(())#空元组 #一个值的元组需在值后加一个逗号&#xff0c;与括号进行区分&#xff1b; b(1)#非元组 c(1,) print(b) print(c)#函…

MIT发布首个贝叶斯「数据清洗」机器人!8小时洗200万条数据

来源&#xff1a;GitHub和数据派THU编辑&#xff1a;王菁校对&#xff1a;林亦霖脏数据可以说是所有AI从业者、数据分析师、数据科学家的噩梦。好消息来了&#xff01;麻省理工学院的研究人员最近带来了一种全新的系统PClean&#xff0c;能够自动地清洗脏数据&#xff0c;如错误…

我的开源项目——Jerry

在日常工作中&#xff0c;经常会碰到一些问题&#xff0c;比如数字金额要写成千分位形式&#xff08;1234 -> 123,4.00&#xff09;、要写成汉字大写形式&#xff08;123 -> 壹佰贰拾叁圆&#xff09;&#xff0c;又比如要进行 cookie 读写操作&#xff0c;这些问题都比较…

python-列表和元组

python 数据结构 1.序列(包括元组、列表、字符串、buffer对象和xrange对象)序列中第一个元素的索引为0&#xff0c;第二个为1&#xff0c;依次类推序列的最后一个元素标记为-1&#xff0c;最后第二个为-2&#xff0c;依次类推 既可以向前计数&#xff0c;也可以向后计数2.列表和…

李德毅院士:希望智能驾驶成为我国继高铁之后又一张新名片

来源&#xff1a;汽车俱乐部Plus/ 导读 /5月19日&#xff0c;在WIC2021第五届世界智能大会的分论坛“智能交通峰会”上&#xff0c;中国工程院院士&#xff0c;欧亚科学院院士李德毅发表了主题演讲。以下是演讲实录。让我们掌声欢迎中国工程院院士&#xff0c;欧亚科学院院士&a…

windows下vagrant的安装使用

vagrant是简便虚拟机操作的一个软件&#xff0c;而使用虚拟机有几个好处&#xff1a; 1、为了开发环境与生产环境一致&#xff08;很多开发环境为windows而生产环境为linux&#xff09;&#xff0c;不至于出现在开发环境正常而移步到正式生产环境时出现各种问题&#xff0c;而v…