东莞网站建设代理wordpress文章怎么生成标签

diannao/2025/10/26 21:32:22/文章来源:
东莞网站建设代理,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,一经查实,立即删除!

相关文章

展示型网站怎么做宿迁市网站建设

我的服务器北京时间,php调用的时间: date.timezone "America/Chicago" 这是美国这边的一个时间,有的时候跟北京相差13个小时,有的时候跟北京时间相差14个小时,所以很不好处理,现在php函数就能处…

大英做网站微信商城后台管理系统

如果我们从集合论(关系代数)的角度来看,一张数据库的表就是一组数据元的关系,而每个 SQL 语句会改变一种或数种关系,从而产生出新的数据元的关系(即产生新的表)。我们学到了什么?思考…

设计师图片素材网站水墨画风格网站

题目描述: 给定任一个各位数字不完全相同的4位正整数,如果我们先把4个数字按非递增排序,再按非递减排序,然后用第1个数字减第2个数字,将得到 一个新的数字。一直重复这样做,我们很快会停在有“数字黑洞”之…

用友加密狗注册网站交河做网站价格

空指针异常是Java中最常见,最烦人的异常。 在这篇文章中,我想避免这种不希望的异常。 首先让我们创建引发空指针异常的示例 private Boolean isFinished(String status) { if (status.equalsIgnoreCase("Finish")) { return Boolean.TRUE; …

手机网页及网站设计蝶恋花直播app下载安装

网关需要维护相关负载的服务器,手动添加相对来说是一件比较麻烦的工作;为了解决这一问题组件扩展了一个基于consul服务发现插件,通过配置这个插件和启用后网关会自动从consul服务中获取服务并添加到网关对应的路由负载规则中。引用插件Bumble…

PS做网站页面尺寸清明节ppt模板免费下载

简述大家都知道,在C语音中指针的地位很重要,各种指针,功能很强大!但是用不好,指针也比较容易出问题。这里介绍的是函数指针的一种应用方法,即使用函数指针来实现消息命令的注册与回调处理。代码测试的处理函…

织梦网站转移福州做网站建设公司

一、对象 1、对象创建 类加载检查 虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池定位到类的符号引用,并且检查这个符号引用代表的类是否被加载、解析和初始化过。若没有,必须先执行类加载过程。分配内存 类加载检查通过后,jvm将为新生对象分配内存,…

外贸网站推广收费襄阳做网站价格

《嵌入式工程师自我修养/C语言》系列——迅速了解Ascii、GBK、Unicode、UTF-8、BCD各种编码格式的由来及关系 一、Ascii编码二、GBK编码三、Unicode编码四、UTF-8编码五、BCD编码六、其他网友的总结 快速学习嵌入式开发其他基础知识?>>>>>>>&g…

网站如何实现qq登录功能网站的类型

有CAE开发商问及OCCT几何内核的网格方面的技术问题。其实,OCCT几何内核的现有网格生成能力比较弱。 HybridOctree_Hex的源代码,还没有仔细去学习。 “HybridOctree_Hex”的开发者说:六面体网格主要是用在数值模拟领域的,比如汽车…

景安怎么把网站做别名原阳网站建设

win7安装nodejs失败 显示This application is only supported on Windows 8.1,Windows Server 2012 R2,or higer. win7安装nodejs失败 在win7重装nodejs,版本是node-v14.16.1-x64,安装时报了win7系统不支持的问题 2021年4月8日 —— 目前除了最新的v14大…

北京企业网站建设费用关于美食的网站设计

文章目录前言:Join背景介绍Join常见分类以及基本实现机制Hash JoinBroadcast Hash JoinShuffle Hash JoinSort-Merge Join总结前言: 写SQL的时候很多时候都有用到join语句,但是我们真的有仔细想过数据在join的过程到底是怎么样的吗&#xff…

网站维护描述建设网站需要体现的流程有哪些

主要有两个注意点 1、columns数组中保留一个对象不设置宽度&#xff0c;其余都要设置宽度&#xff1b; 2、HTML中scroll要设置为{x: 足够长的固定宽度}&#xff1b; 具体代码如下&#xff1a; <a-tableref"table"size"middle"rowKey"rowSerial&qu…

苏州网站seo服务空白网站怎么建立

文章目录 第1章 统计学习方法概论1.1 统计学习1&#xff0e;统计学习的特点2&#xff0e;统计学习的对象3&#xff0e;统计学习的目的4&#xff0e;统计学习的方法1.2.1 基本概念1.2.2 问题的形式化 1.3 统计学习三要素1.3.1 模型1.3.2 策略1.3.3 算法 1.4 模型评估与模型选择1…

网页和网站的区别工作计划如何写

什么是死锁 死锁&#xff0c;简单来说就是两个或者多个的线程在执行的过程中&#xff0c;争夺同一个共享资源造成的相互等待的现象。如果没有外部干预线程会一直阻塞下去. 导致死锁的原因 互斥条件&#xff0c;共享资源 X 和 Y 只能被一个线程占用; 请求和保持条件&#xf…

棋牌网站哪里做seo名词解释

回归预测 | MATLAB实现GWO-DHKELM基于灰狼算法优化深度混合核极限学习机的数据回归预测 &#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现GWO-DHKELM基于灰狼算法优化深度混合核极限学习机的数据回归预测 &#xff08;多指标&#xff0c;多图&#…

个人档案网站该怎么做修改wordpress标题图片

docker小白第十一天 dockerfile分析 Dockerfile是用来构建Docker镜像的文本文件&#xff0c;是由一条条构建镜像所需的指令和参数构成的脚本。即构建新镜像时会用到。 构建三步骤&#xff1a;编写dockerfile文件-docker build命令构建镜像-docker run镜像 运行容器实例。即一…

如何做网盟推广网站智能科技网站模板下载

在北京活跃着一支名为“牧人”的合唱组织。成员由曾经在内蒙下乡的北京知青、在京的蒙古族人和热爱蒙古文化的其他民族组成。合唱团成立于2004年11月20日。春节前&#xff0c;我有幸参加了他们的一次聚会&#xff0c;感触颇深。这是一个非常团结&#xff0c;亲如一家的洋溢着火…

网站备案完毕 怎样建设网站禄丰网站建设

指定某网站内容&#xff1a;site:xxx 屏蔽某网站内容&#xff1a;-site:xxx 例如&#xff1a;搜 springboot项目demo&#xff0c;你想只搜csdn&#xff0c;屏蔽掉博客园&#xff0c;51cto&#xff0c;等&#xff0c;在输入框中输入如下&#xff1a; springboot项目demo site:c…

专业网站建设制作价格低网站规划设计报告

论文一 论虚拟化网络架构的规划与建设 随着信息技术的发展,网络以及软件厂商的产品、企业网络的规划按照NaaS模型进行演进已经成为一种共识。在NaaS的理念下,企业的IT专业人员将能够从选项菜单中订购网络基础设施组件,根据业务需求进行设计,并在短时间内交付和运行整个网…

网站开发系统简介作文网小学

一、简述 您可以在数组数据结构中存储相同类型的多个变量。您可以通过指定数组元素的类型来声明数组。如果您希望数组存储任何类型的元素&#xff0c;您可以指定object其类型。在 C# 的统一类型系统中&#xff0c;所有类型&#xff08;预定义的和用户定义的、引用类型和值类型&…