LL(1)语法分析程序设计

【实验目的】

1.熟悉判断LL(1)文法的方法及对某一输入串的分析过程。

2.学会构造表达式文法的预测分析表。

【实验内容】

编写一个语法分析程序,对于给定的输入串,能够判断识别该串是否为给定文法的句型。

【实验要求】

  1. 输入一个LL(1)文法,构造相应的LL(1)预测分析表。
  2. 从键盘读入输入串,由算法判断该输入串是否为该文法的句子,若正确,就通过,若错误就报错。

【参考说明】

语法分析主要是将从词法分析那里得来的记号构成一棵语法树。例:

SHMA#
 adbe# 
 S->aH
 H->aMd
 H->d
 M->Ab
 M->@
 A->aM
 A->e
 分析例句:aaabd#  

/*
a d b e

S H M A

S->aH
H->aMd
H->d
M->Ab
M->@
A->aM
A->e
end

aaabd#

*/
#include<iostream>
#include<string>
#include<map>
#include<vector>
#include<stack>
#include<set>
#include<cstring>
using namespace std;
map<char, int>getnumber;//map函数是一种映射,将任何基本类型映射到任何基本类型
char yszf[100]; //获得对应字符
vector<string>proce; //长度根据需要自动改变的数组
int database[500][500]; //预测分析表
int number = 0;
int numbervt = 0; //numvt是终结符集合,0是'#',numvt表空字
string first[100];//FIRST集合
string follow[200];//FOLLOW集合
void readall()//先加入终结符,再加入非终结符
{
    memset(database, -1, sizeof(database));//将database中所有的字符用-1替换并返回table
    getnumber['#'] = 0;//把#映射给0
    yszf[0] = '#';//#储存在第一个数组
    cout << "请输入所有的终结符:" << endl;
    char m;
    do
    {
        cin >> m;
        getnumber[m] = ++number;//number从0开始计数,把yszf数组装满
        yszf[number] = m;//将m赋值给第number个数组
    } while (cin.peek() != '\n');//循环直到碰到换行符结束
    numbervt = ++number;//numbervt比number大1
    getnumber['@'] = numbervt; //空字
    yszf[number] = ('@');//numbervt表示空字
    cout << "请输入所有非终结符:" << endl;
    do//一样的循环结构
    {
        cin >> m;
        getnumber[m] = ++number;
        yszf[number] = m;
    } while (cin.peek() != '\n');//循环直到碰到换行符结束
    cout << "输入产生式集合(空字用'@'表示),以'end'结束:" << endl;
    string pro;
    while (cin >> pro&&pro != "end")//字符串pro以end结束
    {
        string xx;
        int i;
        xx += pro[0];
        for (i = 3; i<pro.size(); i++)
        {
            if (pro[i] == '|')//当pro中有|时
            {
                proce.push_back(xx);//push_back在vector尾部加入一个数据
                xx.clear(); //删除ss中的所有元素
                xx += pro[0];//重新开始赋值
            }
            else
            {
                xx += pro[i];//其他时候就直接赋值给ss
            }
        }
        proce.push_back(xx);
    }
}
void jjjh(string &a, string b) //a=a or b 取a,b交集赋值给a
{
    set<char>dd;//插入删除操作时仅仅需要指针操作节点即可完成,不涉及到内存移动和拷贝,不允许有重复元素,自动被排序
    for (int i = 0; i<a.size(); i++)
        dd.insert(a[i]);//从a的开头开始插入,将a的所有字符全部插入到se
    for (int i = 0; i<b.size(); i++)//重复元素自动被删掉,并且se字符自动排序
        dd.insert(b[i]);//从b的开头开始插入,将b的所有字符全部插入到se
    string uu;
    set<char>::iterator yy;//迭代器
    for (yy = dd.begin(); yy != dd.end(); yy++)
        uu += *yy;//begin()就是指向容器第一个元素的迭代器,end()是指向容器最后一个元素的下一个位置的迭代器
    a = uu;//最后赋值给a
}
string getuu(int vn, int & determine) //vn能推出的不含空字的vt集合,并且判断vn能否推出空字
{
    int i;
    if (vn == numbervt)
        determine = 1;//numbervt为空字,vn为空,determine赋一个值为1,推出空字就进行循环
    if (vn<numbervt)
        return first[vn];//如果vn<numbervt就返回vn的FIRST集合,推不出空字直接返回
    string uu;
    for (i = 0; i<proce.size(); i++)
    {
        if (getnumber[proce[i][0]] == vn)//getnumber获得proce数组中的每一行的第一个字符如果与vn匹配就进行迭代getuu函数,下一次循环就一直是每一行的第二个字符
            uu += getuu(getnumber[proce[i][1]], determine);
    }
    return uu;//进行到这一步uu的值为1
}
void getfirst()
{
    for (int i = 1; i <= numbervt; i++) //1到numvt属于终结符,终结符的first集合是其本身
    {
        first[i] += ('0' + i);//空字代表0,其他字符都记为数字
    }
    for (int j = 0; j<proce.size(); j++) //扫描所有产生式
    {
        int k = 0; int determine = 0; //k扫描该产生式
        do{
            determine = 0;
            k++;
            if (k == proce[j].size()) //推到最后一个了,则附加空字
            {
                first[getnumber[proce[j][0]]] += ('0' + numbervt);//用0加上从终结符之后开始的数字将其赋值给FIRST集合
                break;
            }
            jjjh(first[getnumber[proce[j][0]]], getuu(getnumber[proce[j][k]], determine));//将get_f得到的字符串与FIRST集合进行交集,最后返回FIRST集合
        } while (determine); //到无法推出空字为止
    }
}
void firstputout()
{
    cout << "first集:" << endl;//输出FIRST集合
    for (int i = 1; i <= number; i++)//从1一直到非终结符的最后全部显示
    {
        cout << "first [" << yszf[i] << "]: ";
        for (int j = 0; j<first[i].size(); j++)
            cout << yszf[first[i][j] - '0'] << " ";
        cout << endl;
    }
    cout << endl;
}
void getfollow()
{
    jjjh(follow[getnumber[proce[0][0]]], "0"); //先给follow集合开始符号添加'#';
    for (int j = 0; j<proce.size(); j++) //扫描所有产生式
    {
        for (int tt = 1; tt<proce[j].size(); tt++) //每个非终结符的follow集合从第二个字符开始判断
        {
            if (getnumber[proce[j][tt]] <= numbervt)
                continue; //终结符没有follow集合
            int k = tt;
            int determine;
            do
            {
                determine = 0;
                k++;
                if (k == proce[j].size()) //都能推出空字,follow集合就等于产生式左边的非终结符的follow集合
                {
                    jjjh(follow[getnumber[proce[j][tt]]], follow[getnumber[proce[j][0]]]);//返回第一个字符的follow集合与后面所有字符的follow集合的交集
                    break;
                }
                jjjh(follow[getnumber[proce[j][tt]]], getuu(getnumber[proce[j][k]], determine));//推不出空字返回follow集合与终结符的first集合的交集
            } while (determine);
        }
    }
}
void followputout()
{
    cout << "follow集:" << endl;
    for (int i = 1+numbervt; i <= number; i++)
    {
        cout << "follow [" << yszf[i] << "]: ";
        for (int j = 0; j<follow[i].size(); j++)
            cout << yszf[follow[i][j] - '0'] << " ";
        cout << endl;
    }
    cout << endl;
}
void getdatabase() //获得预测分析表
{
	for (int i = 0; i<proce.size(); i++) //扫描所有产生式
    {
    	if (proce[i][1] == '@') //直接推出空字的,特判下(follow集合等于产生式左边的非终结符中元素填)
        {
            string kkk = follow[getnumber[proce[i][0]]];//FOLLOW集合的每一行的第一个字符赋值给kkk
            for (int k = 0; k<kkk.size(); k++)
            {
                database[getnumber[proce[i][0]]][kkk[k] - '0'] = i;//将i赋值给database行数为getnumber中第i行的产生式的下标和列数为kkk中减去0的下标
            }
        }
        string ggg = first[getnumber[proce[i][1]]];
        for (int j = 0; j<ggg.size(); j++) //检查FIRST集合
        {
            if (ggg[j] != ('0' + numbervt))
            {
                database[getnumber[proce[i][0]]][ggg[j] - '0'] = i;
            }
            else //有空字的,检查follw集合
            {
                string kkk = follow[getnumber[proce[i][1]]];
                for (int k = 0; k<kkk.size(); k++)
                {
                    database[getnumber[proce[i][0]]][kkk[k] - '0'] = i;
                }
            }
        }
    }
}
string ifproce(int i) //由对应下标获得对应产生式
{
    if (i<0)
    return " "; //无该产生式
    string pl;
    pl += proce[i][0];
    pl += "->";
    for (int j = 1; j<proce[i].size(); j++)
        pl += proce[i][j];
    return pl;
}
void databaseputout()
{
    cout << "预测分析表:" << endl;
    for (int i = 0; i<numbervt; i++)
        cout << '\t' << yszf[i];//非终结符的打印
    cout << endl;
    for (int i = numbervt + 1; i <= number; i++)
    {
        cout << yszf[i];//先打印一个终结符,在这一行的接下来打印分析内容
        for (int j = 0; j<numbervt; j++)
        {
            cout << '\t' << ifproce(database[i][j]);
        }
        cout << endl;
    }
    cout << endl;
}
string word;
bool analyze() //总控,分析字word的合法性,若合法,输出所有产生式
{
    stack<char>box;
    box.push('#');
    box.push(proce[0][0]);//将#入栈,将proce[0][0]入栈
    int i = 0;
    while (!box.empty())//检测是否为空返回bool
    {
        int sor = box.top();//获得栈顶元素
        box.pop();//弹出栈顶元素
        if (sor == word[i]) //是终结符就推进
        {
            i++;
        }
        else if (sor == '#') //成功就结束
        {
            return 1;
        }
        else if (database[getnumber[sor]][getnumber[word[i]]] != -1) //查表
        {
            int k = database[getnumber[sor]][getnumber[word[i]]];
            cout << proce[k][0] << "->";//第一个字符输出->
            for (int j = 1; j<proce[k].size(); j++)
                cout << proce[k][j];//输出proce数组
            cout << endl;
            for (int j = proce[k].size() - 1; j>0; j--) //按照逆序入栈
            {
                if (proce[k][j] != '@')//不等于空就入栈
                    box.push(proce[k][j]);
            }
        }
        else //失败!
        {
            return 0;
        }
    }
    return 1;
}
int main()
{
    readall();//调用读取终结符与非终结符的函数
    getfirst();//调用得到FIRST集合的函数
    getfollow();//调用得到FOLLOW集合的函数
    getdatabase();//调用得到预测分析表的函数
    firstputout();//调用打印FIRST集合的函数
    followputout();//调用打印FOLLOW集合的函数
    databaseputout();//调用打印预测分析表的函数
    cout << "请输入句子:" << endl;
    cin >> word;
    while(word!="end")
    {
    	if (analyze())
        	cout << "succeed!该句子是文法给定的句子,所用的产生式如上" << endl;
    	else cout << "error!该句子不是文法给定的句子" << endl;
        cout << "请输入句子或以'end'结束程序:" << endl;
    	cin >> word;
	}
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值