语法分析-算术表达式-C++实现

本文介绍了一种使用C++实现的算术表达式语法分析方法,采用递归下降法结合预测分析表来识别算术表达式的正确性,并提供了详细的程序代码及运行结果。

语法分析-算术表达式-C++实现

1.1 算术表达式文法规则:

E->TE’

E’->+TE’|e

T->FT’

T’->*FT’|e

F->i|(E)

1.2 符号集:

² First集:

\1. First(F)={ ( , id }

\2. First(T) { ( , id }

\3. First(T’)={ * , ε }

\4. First(E’)={ + , ε }

\5. First(E)=First(T)={ ( , id }

² Follow集

\1. FOLLOW(E’) = { $ , ) }

\2. FOLLOW(T)= { $ , ) , + }

\3. FOLLOW(T’)= { $ , ) , + }

\4. FOLLOW(F)= { $ , + , ) , * }

\5. FOLLOW(E)={ $ , ) }

² Select集

\1. Select(E --> TE’) = { (, i}

\2. Select(E’ --> à +TE) = { + }

\3. Select(E’ --> ε) = { ) , # }

\4. select(T --> FT’) = { (, i}

\5. select(T’ --> *FT’) = { * }

\6. select(T’ --> ε) = { + , ), #}

\7. select(F --> (E)) = { ( }

\8. select(F --> i) = {i}

1.3 预测分析表

在这里插入图片描述

1.4 程序设计

我用的是递归下降法,当我得到这个预测分析表后,我为每个非终结符设计一个函数,在内部通过判断读入的非终结符判断该用哪一个产生式进行替换,而主程序中就是最左推导,通过调用最左边的非终结符的递归子程序,让整个过程链式执行下去,最后能得到输入的算术表达式,则识别成功

C++代码:****

#include <iostream>
#include <cstring>

#define INPUTMAX 30
#define RESLUTMAX 100

using namespace std;

char InputString[INPUTMAX];      //保存输入的字符串,用数组更方便,直接更改下标就可以往右扫描下一个
string ResultString[RESLUTMAX];  //保存每一次的中间推导过程
int ptr = 0;                     //当前输入符号的下标
int Result_Length = 0;           //ResultString的长度

/*
    递归子程序法我觉得也可以称之为递归的预测分析法,如何选择产生式是看预测分析表的
    也就是为每个非终结符设计一个函数,让它自己选择相应的产生式
    从开始符号推导的过程中,遇到相应的非终结符就调用其函数
*/
void E();

void T();

void E1();

void F();

void T1();

void UpdateResult(string non_terminal, string replace_str);  //用于添加每一次的中间推导式
void ShowResult();  //显示所有中间推导式
void Deal_Production(char production[]);  //接受产生式,该函数把它分解成左边和右边,并调用UpdateResult(),防止代码冗余
void Trim(char s[]);

int main() {
    cout << "请输入以#结尾的字符串,无空格" << endl;

    cin.getline(InputString, INPUTMAX);  //如果直接cin>>会遇到第一个空格就返回

    Trim(InputString); //去除空格,并看他有没有#结尾

    E();  //start

    //string在C++中可以直接两个等号判断相等
    if (ResultString[Result_Length - 1] == InputString)  //如果最后一个推导式就是输入串
        cout << "\n接受成功" << endl;
    else
        cout << "\n不接受";

    ShowResult();  //显示推导式

    return 0;
}

void E() {
    //用if主要是防止无限推导,因为找不到某个字符
    if (InputString[ptr] == 'i' || InputString[ptr] == '(')  //在预测分析表中只有 i,(, E才有相应产生式
    {
        Deal_Production("E --> TE'");
        T();    //调用所有非终结符的相应的函数
        E1();
    }
}

void T() {
    if (InputString[ptr] == 'i' || InputString[ptr] == '(') {
        Deal_Production("T --> FT'");
        F();
        T1();
    }
}

void E1() {
    if (InputString[ptr] == '+') {
        Deal_Production("E' --> +TE'");
        ptr++;  //匹配到一个+,扫描下一个输入符号
        T();  //一定要记得调用产生式右边所有非终结符的函数,并且按顺序
        E1();
    } else if (InputString[ptr] == ')' || InputString[ptr] == '#')
        Deal_Production("E' --> ε");
}

void T1() {
    if (InputString[ptr] == '*') {
        Deal_Production("T' --> *FT'");
        ptr++;
        F();
        T1();
    } else if (InputString[ptr] == ')' || InputString[ptr] == '#' || InputString[ptr] == '+')
        Deal_Production("T' --> ε");
}

void F() {
    if (InputString[ptr] == 'i') {
        Deal_Production("F --> i");
        ptr++;
    } else if (InputString[ptr] == '(') {
        Deal_Production("F --> (E)");
        ptr++;
        E();
    }
}

void UpdateResult(string non_terminal, string replace_str) {
    if (Result_Length == 0)  //开始的时候要初始化,加个开始符号
    {
        string str = non_terminal;
        ResultString[Result_Length++] = str;
        UpdateResult(non_terminal, replace_str);
    } else {
        string str = ResultString[Result_Length - 1];  //先获取上次的推导式
        int index = str.find(non_terminal);     //找到需要替换的终结符的下标
        string str1 = str.substr(0, index);      //获取终结符前的子串
        string str2 = str.substr(index + non_terminal.length());  //获取终结符后的子串
        if (replace_str != "ε")
            str1 += replace_str;  //如果替换为ε,该符号是不用加进推导式中
        str1 += str2;           //拼接起来
        if (str1 + "#" == InputString)  //如果推导式后跟#就等于输入串,最好还是不要直接==InputString
            str1 += "#";        //那么就在其后面加一个#,方便直接判断相等
        ResultString[Result_Length++] = str1;  //推导式加进数组中
    }
}

void ShowResult() {
    cout << "----------------" << endl;
    cout << "推导过程:" << endl;
    for (int i = 0; i < Result_Length; i++) {
        cout << ResultString[i] << endl;
    }
}

void Deal_Production(char production[]) {
    string str = production;  //把数组转化成string可以用很多函数
    int front_index = str.find(' '); //第一个空格出现的位置
    int back_index = str.find('>');

    string str1 = str.substr(0, front_index);  //截取左边
    string str2 = str.substr(back_index + 2, str2.length() - back_index - 1);  //截取后面

    cout << production << endl;

    UpdateResult(str1, str2);
}

void Trim(char s[]) {
    string str = s;
    string str1, str2;
    while (true) {
        int flag = str.find(' ');
        if (flag == str.npos)  //即没找到空格
            break;
        str1 = str.substr(0, flag);
        str2 = str.substr(flag + 1, str.length() - flag - 1);
        str1 += str2;
        str = str1;
    }
    for (int i = 0; i < INPUTMAX; i++)
        s[i] = '\0';   //清空
    //但是这种复制有缺陷,比如当str长度不如s时,实际上s后面的字符保留着,是直接加了个\0隔离开
    //例如 把{1,2,3}复制给{1,2,3,4}就会变成{1,2,3,'\0',4};  而strlen是\0前面的长度
    strcpy(s, str.c_str());  //把string复制给数组是这么写的

    if (s[strlen(s) - 1] != '#')
        s[strlen(s)] = '#';
}

运行结果:
在这里插入图片描述

评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值