SDUT【编译原理】D - 表达式语法分析——递归子程序法

快要机测了,被逼无奈开始做题。。。

1. 思路概述:

言归正传,这道题有几个要注意的点:

  1. 没有使用到FIRST和FOLLOW集合,举个例子:
PROCEDURE  A;
BEGIN
	IF  SYM in FIRST(TE’) THEN
           BEGIN  T;E'  END
	ELSE IF  SYM in FIRST(BC) THEN
		     BEGIN  B; C  END
    ELSE IF  SYM in FOLLOW(A) THEN
		     BEGIN  END
	ELSE ERROR
END;
//这里找不到“属于”符号..先用“in”替代了

  对于能推出空串的非结束符对应的子程序,可以通过判断该非结束符号的FOLLOW集合是否存在目标字符SYM来判断要不要采用空串推导;而这道题的测试点并没有体现这一点,不进行判断直接推导空串反而更符合本题的用例(或许是作者为了减少一些代码量。。。)。

  1. 进行语法分析前要先判断首字符是否为终结符,虽然我们的程序可以自己判断 输入串 是否是正确的文法,但是前面两步推导 E-->TG, T-->FS输入串 无关,因此无论 输入串 的首位是什么都会执行这两步,但是测试点可不允许,所以语法分析前我们要手动筛去首字符就不符合文法的情况。

  2. 对F的子程序要进行一些特殊处理,因为 F-->(E) | i 中存在可以直接判断 输入串 不合规则的能力,像E,T完全由调用其他子程序组成,将问题抛给其他子程序;而G,S有空串推导,可以通过推导空串来将问题抛给上一个调用它的子程序(因为这道题无法通过FOLLOW集合来超前判断);而F有两种确定的判断 输入串 不合理的情况:1. 目标字符SYM不属于F的任意一个候选式的FIRST集合;2. 调用E后没有右括号闭合;此时直接打印error并使用 exit(0) 结束程序即可。

2. 代码结构:

  1. 使用一个Buffer类来进行全局控制,相比全局变量结构更严谨;类设计如下:
    Buffer类设计

buffer:存储输入串;
_border:输入串的边界;
_ptr:当前目标字符;
_count:当前行;

Advance():上一个字符匹配成功后,前进到下一个字符位置;
Push():读取输入串;
Count():更新当前行;
Now():读取当前目标字符;
End():判断程序结束后是否到达#

  1. 五个子程序:
void CallE(Buffer*);
void CallT(Buffer*);
void CallG(Buffer*);
void CallS(Buffer*);
void CallF(Buffer*);

3. 主体代码:


#include <iostream>
#include <string>
#include <set>

class Buffer {
private:
    char buffer[100];
    //最后一个元素的_borde-1
    int _border = 0;
    int _ptr = 0;
    int _count = 0;
public:
    void Advance() {
        this->_ptr++;
    }

    void Push(char ch) {
        buffer[_border++] = ch;
    }

    void Count() {
        std::cout << this->_count<<" ";
		this->_count++;
    }

    char Now() {
		return buffer[_ptr];
    }

    bool End() {
        return _ptr == (_border-1);
	}

};

void CallE(Buffer*);
void CallT(Buffer*);
void CallG(Buffer*);
void CallS(Buffer*);
void CallF(Buffer*);

std::set<char> followE = { ')','#' };
std::set<char> followG = { ')','#' };
std::set<char> followT = { '+',')','#' };
std::set<char> followS = { '+',')','#' };
std::set<char> followF = { '*','+','#',')' };

std::set<char> firstE = { '(','i' };
std::set<char> firstG = { '+','&' };
std::set<char> firstT = { '(','i' };
std::set<char> firstS = { '*','&' };
std::set<char> firstF = { '(','i' };

std::set<char> Vn = { 'E','G','T','S','F' };
std::set<char> Vt = { 'i','+','(',')','*'};

void CallE(Buffer* buffer) {
    buffer->Count();
    std::cout << "E-->TG" << std::endl;
    CallT(buffer);
    CallG(buffer);
}

void CallT(Buffer* buffer) {
    buffer->Count();
    std::cout << "T-->FS" << std::endl;
    CallF(buffer);
    CallS(buffer);
}

void CallG(Buffer* buffer) {
    if (buffer->Now() == '+'){
        buffer->Count();
        std::cout << "G-->+TG" << std::endl;
        buffer->Advance();
        CallT(buffer);
        CallG(buffer);
    }
    else {
        buffer->Count();
        std::cout << "G-->&" << std::endl;
    }

}

void CallS(Buffer* buffer) {
    if (buffer->Now() == '*'){
        buffer->Count();
        std::cout << "S-->*FS" << std::endl;
        buffer->Advance();
        CallF(buffer);
        CallS(buffer);
    }
    else {
        buffer->Count();
        std::cout << "S-->&" << std::endl;
	}

}

void CallF(Buffer* buffer) {
    if (buffer->Now() == '(') {
        buffer->Count();
        std::cout << "F-->(E)" << std::endl;
		buffer->Advance();
        CallE(buffer);
        if (buffer->Now() == ')') {
			buffer->Advance();
        }
        else {
            std::cout << "error" << std::endl;
            exit(0);
        }
    }
    else if(buffer->Now() == 'i'){
        buffer->Count();
        std::cout << "F-->i" << std::endl;
        buffer->Advance();
    }
    else {
        std::cout << "error" << std::endl;
        exit(0);
    }

}

int main()
{
    Buffer* buffer = new Buffer();
    std::string s = "";
    getline(std::cin, s);

    for (char c : s) {
        buffer->Push(c);
        //std::cout << c;
    }
    if (Vt.count(s[0]) == 0) {
        std::cout << "error";
        return 0;
    }

	CallE(buffer);
    if (buffer->End())
        std::cout << "accept";
    else
		std::cout << "error";
    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值