编译原理实验(三):自下而上语法分析-根据 PL/0 语言的文法规范,编写 PL/0 语言的语法分析程序

**

任务说明

**
1. 实验目的
  给出 PL/0 文法规范,要求编写 PL/0 语言的语法分析程序。
  通过设计、编制、调试一个典型的自下而上语法分析程序,实现对词法分析程序所提供的单词序列进行语法检查和结构分析,进一步掌握常用的语法分析方法。
  选择最有代表性的语法分析方法,如算符优先分析法、LR 分析法;或者调研语法分析器的自动生成工具 YACC 的功能与工作原理,使用YACC 生成一个自底向上的语法分析器。
2. 实验准备
  微机安装好 C 语言,或 C++,或 Visual C++.
3.实验内容
  已给 PL/0 语言文法,构造表达式部分的语法分析器。
  分析对象〈算术表达式〉的 BNF 定义如下:
    <表达式> ::= [+|-]<项>{<加法运算符> <项>}
    <项> ::= <因子>{<乘法运算符> <因子>}
    <因子> ::= <标识符>|<无符号整数>| ‘(’<表达式>‘)’
    <加法运算符> ::= +|-
    <乘法运算符> ::= *|/
    <关系运算符> ::= =|#|<|<=|>|>=
4. 实验要求
  将实验一“词法分析”的输出结果,作为表达式语法分析器的输入,进行语法解析,对于语法正确的表达式,报告“语法正确”;对于语法错误的表达式,报告“语法错误”, 指出错误原因。
  把语法分析器设计成一个独立一遍的过程。
  采用算符优先分析法或者 LR 分析法实现语法分析;或者调研语法分析器的自动生成工具 YACC 的功能与工作原理,使用YACC 生成一个自底向上的语法分析器。
5. 输入输出
 输入:
  PL/0 表达式,用实验一的输出形式作为输入。 例如: 对于 PL/0 表达式,(a+15)b 用下列形式作为输入:
  (lparen,()
  (ident,a)
  (plus,+)
  (number,15)
  (rparen,))
  (times,
)
  (ident,b)
 输出:
  对于语法正确的表达式,报告“语法正确”;
  对于语法错误的表达式,报告“语法错误”, 指出错误原因。

具体实现

设计思想

扩充的巴科斯范式
  <表达式> ::= [+|-]<项>{<加法运算符> <项>}
  <项> ::= <因子>{<乘法运算符> <因子>}
  <因子> ::= <标识符>|<无符号整数>| ‘(’<表达式>‘)’
  <加法运算符> ::= +|-
  <乘法运算符> ::= |/
普通的巴科斯范式
  为表示方便:
  表达式E、项X、因子Y、标识符b,无符号整数z,加法运算符A,乘法运算符C
    E->AX|X|EAX
    X->Y|XCY
    Y->b|z|(E)
    A->+|-
    C->
|/
项目集&识别活前缀的DFA
在这里插入图片描述
其中I1,I3,I10,I18存在归约-移进冲突。

求解所有非终结符的FOLLOW集合
  Follow(S`)={#}
  Follow(E)={#,),+,-}
  Follow(X)={),+,-,#,,/}
  Follow(Y)={),+,-,#,
,/}
  Follow(A)={b,z,(}
  Follow©={b,z,(}
 I1,I3,I10,I18的冲突可以消解,所以该文法是SLR(1)文法。

分析表
  (0)S`->E
  (1)E->AX
  (2)E->X
  (3)E->EAX
  (4)X->Y
  (5)X->XCY
  (6)Y->b
  (7)Y->z
  (8)Y->(E)
  (9)A->+
  (10)A->-
  (11)C->*
  (12)C->
 在这里插入图片描述

算法流程

main函数相当于总控程序,Analy_action函数对应action表,Analy_goto函数对应goto表。
在这里插入图片描述

代码

#include <iostream>
#include <string>
#include<stack>
#include<vector>
using namespace std;
int flag=0; //记录分析状态
int p=-1;//指针作用 当前扫描单词的下标
stack <int> State;  //状态栈
stack <string> in;  //符号栈
int error=0;//错误处理 当置1时就报错结束分析
//分析的单词,s1为编码,s2为单词符号
struct strs
{
   
    string s1,s2;
};

strs analystr;//当前分析的单词

//扫描下一个单词
void Advance(strs *S)
{
   
    p++;
    analystr = S[p];
}
//对应goto表,执行归约后,根据当前状态和符号确定哪一个状态应该进入State中
void Analy_goto()
{
   
    int state=State.top(); //取当前栈顶状态
    string si=in.top(); //当前符号栈的栈顶符号
    char sii=si[0];  //string->char转换
    switch(state)
    {
   
    //根据goto表,压入状态的代码
    case 0:
        if(sii=='E') State.push(1);
        else if(sii=='X') State.push(3);
        else if(sii=='Y') State.push(6);
        else if(sii=='A') State.push(2);
        else error=1;
        break;
    case 1:
        if(sii=='A') State.push(17);
        else error=1;
        break;
    case 2:
        if(sii=='X') State.push(10);
        else if(sii=='Y') State.push(6);
        else error=1;
        break;
    case 3:
        if(sii=='C') State.push(11);
        else error=1;
        break;
    case 9:
        if(sii=='E') State.push(14);
        else if(sii=='X') State.push(3);
        else if(sii=='Y') State.push(6);
        else if(sii=='A') State.push(2);
        else error=1;
        break;
    case 10:
        if(sii=='C') State.push(11);
        else error=1;
        break;
    case 11:
        if(sii=='Y') State.push(15);
        else error=1;
        break;
    case 14:
        if(sii=='A') State.push(17);
        else error=1;
        break;
    case 17:
        if(sii=='X') State.push(18);
        else if(sii=='Y') State.push(6);
        else error&#
  • 4
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
语法分析编译原理中的重要环节,它的主要任务是将词法分析器输出的词法单元序列转换为语法分析树或语法分析图,以便于后续的语义分析和代码生成。语法分析器的实现方式有多种,其中最常用的是基于文法的自上而下的递归下降分析法和基于文法自下而上的移进-归约分析法。 在本实验中,我们将使用C++语言实现一个简单的递归下降分析法的语法分析器,实现对类C语言的一个子集进行语法分析。该子集包含了整型变量声明、赋值语句、算术表达式、条件语句和循环语句等基本语法结构。 1. 文法定义 我们定义的子集语法规则如下: ``` <program> ::= <stmt_list> <stmt_list> ::= <stmt> | <stmt> <stmt_list> <stmt> ::= <decl_stmt> | <assign_stmt> | <if_stmt> | <while_stmt> <decl_stmt> ::= int <id>; <assign_stmt> ::= <id> = <expr>; <if_stmt> ::= if (<condition>) <stmt> <while_stmt> ::= while (<condition>) <stmt> <condition> ::= <expr> <rel_op> <expr> <expr> ::= <term> | <term> <add_op> <expr> <term> ::= <factor> | <factor> <mult_op> <term> <factor> ::= <int> | <id> | (<expr>) <id> ::= <letter> | <id> <letter> | <id> <digit> <int> ::= <digit> | <int> <digit> <letter> ::= a | b | ... | z | A | B | ... | Z <digit> ::= 0 | 1 | ... | 9 <add_op> ::= + | - <mult_op> ::= * | / <rel_op> ::= < | > | <= | >= | == | != ``` 其中,<program>是整个程序的入口,<stmt_list>表示语句列表,<stmt>表示语句,<decl_stmt>表示变量声明语句,<assign_stmt>表示赋值语句,<if_stmt>表示条件语句,<while_stmt>表示循环语句,<condition>表示条件表达式,<expr>表示算术表达式,<term>表示项,<factor>表示因子,<id>表示标识符,<int>表示整数常量,<letter>表示字母,<digit>表示数字,<add_op>表示加减运算符,<mult_op>表示乘除运算符,<rel_op>表示关系运算符。 2. 代码实现 在实现递归下降分析法的语法分析器时,我们需要实现对以上语法规则的递归下降分析函数,每个函数对应一个语法规则。 首先,我们需要定义一个词法分析器,用于将源代码转换为词法单元序列。在本实验中,我们将使用一个简单的词法分析器,它可以处理int关键字、标识符、整数常量、加减乘除运算符、关系运算符、左右括号和分号等词法单元。 ```c++ #include <iostream> #include <string> #include <vector> #include <stdexcept> using namespace std; // 定义词法单元类型 enum TokenKind { TK_INT, // int关键字 TK_ID, // 标识符 TK_NUM, // 整数常量 TK_PLUS, // + TK_MINUS, // - TK_MUL, // * TK_DIV, // / TK_LT, // < TK_GT, // > TK_LE, // <= TK_GE, // >= TK_EQ, // == TK_NE, // != TK_LPAREN, // ( TK_RPAREN, // ) TK_SEMICOLON // ; }; // 定义词法单元结构体 struct Token { TokenKind kind; // 词法单元类型 string str; // 词法单元字符串 }; // 定义词法分析器 class Lexer { public: Lexer(const string& source) : src(source), pos(0) {} // 获取下一个词法单元 Token getNextToken() { // 跳过空白字符 while (isspace(src[pos])) pos++; // 处理数字 if (isdigit(src[pos])) { string numStr; while (isdigit(src[pos])) { numStr += src[pos++]; } return { TK_NUM, numStr }; } // 处理标识符 if (isalpha(src[pos])) { string idStr; while (isalnum(src[pos])) { idStr += src[pos++]; } return { TK_ID, idStr }; } // 处理运算符和括号 switch (src[pos]) { case '+': pos++; return { TK_PLUS, "+" }; case '-': pos++; return { TK_MINUS, "-" }; case '*': pos++; return { TK_MUL, "*" }; case '/': pos++; return { TK_DIV, "/" }; case '<': if (src[pos + 1] == '=') { pos += 2; return { TK_LE, "<=" }; } else { pos++; return { TK_LT, "<" }; } case '>': if (src[pos + 1] == '=') { pos += 2; return { TK_GE, ">=" }; } else { pos++; return { TK_GT, ">" }; } case '=': if (src[pos + 1] == '=') { pos += 2; return { TK_EQ, "==" }; } else { throw runtime_error("invalid token"); } case '!': if (src[pos + 1] == '=') { pos += 2; return { TK_NE, "!=" }; } else { throw runtime_error("invalid token"); } case '(': pos++; return { TK_LPAREN, "(" }; case ')': pos++; return { TK_RPAREN, ")" }; case ';': pos++; return { TK_SEMICOLON, ";" }; default: throw runtime_error("invalid token"); } } private: string src; // 源代码 size_t pos; // 当前位置 }; ``` 接下来,我们依次实现递归下降分析函数。函数的实现方式为:首先获取当前词法单元,然后根据语法规则进行分析,如果符合规则则继续获取下一个词法单元,否则抛出异常。 ```c++ class Parser { public: Parser(const string& source) : lexer(source) {} // 解析程序 void parseProgram() { parseStmtList(); } private: // 解析语句列表 void parseStmtList() { parseStmt(); Token token = lexer.getNextToken(); if (token.kind != TK_SEMICOLON) { throw runtime_error("missing semicolon"); } if (token.kind != TK_EOF) { parseStmtList(); } } // 解析语句 void parseStmt() { Token token = lexer.getNextToken(); switch (token.kind) { case TK_INT: parseDeclStmt(); break; case TK_ID: parseAssignStmt(); break; case TK_IF: parseIfStmt(); break; case TK_WHILE: parseWhileStmt(); break; default: throw runtime_error("invalid statement"); } } // 解析变量声明语句 void parseDeclStmt() { Token token = lexer.getNextToken(); if (token.kind != TK_ID) { throw runtime_error("missing identifier"); } token = lexer.getNextToken(); if (token.kind != TK_SEMICOLON) { throw runtime_error("missing semicolon"); } } // 解析赋值语句 void parseAssignStmt() { Token token = lexer.getNextToken(); if (token.kind != TK_ASSIGN) { throw runtime_error("missing assignment operator"); } parseExpr(); token = lexer.getNextToken(); if (token.kind != TK_SEMICOLON) { throw runtime_error("missing semicolon"); } } // 解析条件语句 void parseIfStmt() { Token token = lexer.getNextToken(); if (token.kind != TK_LPAREN) { throw runtime_error("missing left parenthesis"); } parseCondition(); token = lexer.getNextToken(); if (token.kind != TK_RPAREN) { throw runtime_error("missing right parenthesis"); } parseStmt(); } // 解析循环语句 void parseWhileStmt() { Token token = lexer.getNextToken(); if (token.kind != TK_LPAREN) { throw runtime_error("missing left parenthesis"); } parseCondition(); token = lexer.getNextToken(); if (token.kind != TK_RPAREN) { throw runtime_error("missing right parenthesis"); } parseStmt(); } // 解析条件表达式 void parseCondition() { parseExpr(); Token token = lexer.getNextToken(); switch (token.kind) { case TK_LT: case TK_GT: case TK_LE: case TK_GE: case TK_EQ: case TK_NE: parseExpr(); break; default: throw runtime_error("missing relational operator"); } } // 解析算术表达式 void parseExpr() { parseTerm(); Token token = lexer.getNextToken(); while (token.kind == TK_PLUS || token.kind == TK_MINUS) { parseTerm(); token = lexer.getNextToken(); } lexer.ungetToken(); } // 解析项 void parseTerm() { parseFactor(); Token token = lexer.getNextToken(); while (token.kind == TK_MUL || token.kind == TK_DIV) { parseFactor(); token = lexer.getNextToken(); } lexer.ungetToken(); } // 解析因子 void parseFactor() { Token token = lexer.getNextToken(); switch (token.kind) { case TK_NUM: case TK_ID: break; case TK_LPAREN: parseExpr(); token = lexer.getNextToken(); if (token.kind != TK_RPAREN) { throw runtime_error("missing right parenthesis"); } break; default: throw runtime_error("invalid factor"); } } private: Lexer lexer; // 词法分析器 }; ``` 3. 测试样例 我们编写以下测试样例,用于测试语法分析器的正确性。 ```c++ int main() { string source = "int a; a = 1 + 2 * 3; if (a < 10) { a = a + 1; } while (a < 20) { a = a + 2; }"; Parser parser(source); parser.parseProgram(); return 0; } ``` 以上测试样例包含了变量声明、赋值语句、算术表达式、条件语句和循环语句等基本语法结构,用于测试语法分析器的正确性。 4. 总结 本实验通过实现一个简单的递归下降分析法的语法分析器,实现了对类C语言的一个子集进行语法分析。递归下降分析法是编译原理中最常用的语法分析方法之一,它的实现方式简单直观,易于理解和实现,但是它存在递归调用深度过大等问题,需要注意优化和调试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值