【FLA学习笔记】6:基于Table Driven(表驱动)的LL(1)语法分析

建立预测分析表

递归便于书写,但代价比较高,表驱动的效率更好。根据上篇的SELECT集去建立预测分析表:
这里写图片描述

程序

Main.cpp

#include"Rely.h"
#define MAXLEN 100
using namespace std;

//****************************************************
char c;//通用 
bool psr=true;//记录语法分析阶段是否出错 

int s_len=0;//读入的字符串长度 
char* s_pre=new char[MAXLEN];//原始串 
int numNow=0;//表驱动的步骤数 

list<int> ls_term;//模拟模式队列:顺序地存终结符种别的list
//list<int>::iterator it;//list的游动迭代器 
list<int>::iterator it_fst;//list的头部迭代器,用来计算下标 
list<string> ls_str;//原串的截断串列表:模拟队列 
//queue<string> qu_str;//原串的截断串队列 
list<string> ls_td;//推导符组成的列表:模拟栈 
//list<string>::iterator it;//一个通用的list游标 
//****************************************************
//语法分析:传入分析栈栈顶元素,模式队列头枚举值 
void ParseFunction(const char* str,int n){
    if(0==strcmp("E",str)){
        if(identifier==n || unsignint==n || lparen==n){
            cout<<"E->IK1";
            ls_td.pop_back();//弹出E
            ls_td.push_back("K1");//压入K1
            ls_td.push_back("I");//压入I
        }else if(add_sub==n){
            cout<<"E->AIK1";
            ls_td.pop_back();//弹出E
            ls_td.push_back("K1");//压入K1
            ls_td.push_back("I");//压入I
            ls_td.push_back("A");//压入A
        }else{
            cout<<"无路可走"<<endl;
            psr=false;
        }
    }else if(0==strcmp("K1",str)){
        if(add_sub==n){
            cout<<"K1->AIK1";
            ls_td.pop_back();//弹出K1
            ls_td.push_back("K1");//压入K1
            ls_td.push_back("I");//压入I
            ls_td.push_back("A");//压入A
        }else if(rparen==n || end==n){
            cout<<"K1->ε";
            ls_td.pop_back();//弹出K1
        }else{
            cout<<"无路可走"<<endl;
            psr=false;
        }
    }else if(0==strcmp("I",str)){
        if(identifier==n || unsignint==n || lparen==n){
            cout<<"I->FK2";
            ls_td.pop_back();//弹出I
            ls_td.push_back("K2");//压入K2
            ls_td.push_back("F");//压入F
        }else{
            cout<<"无路可走"<<endl;
            psr=false;
        }
    }else if(0==strcmp("K2",str)){
        if(mul_div==n){
            cout<<"K2->MFK2";
            ls_td.pop_back();//弹出K2
            ls_td.push_back("K2");//压入K2
            ls_td.push_back("F");//压入F
            ls_td.push_back("M");//压入M
        }else if(add_sub==n || rparen==n || end==n){
            cout<<"K2->ε";
            ls_td.pop_back();//弹出K2
        }else{
            cout<<"无路可走"<<endl;
            psr=false;
        }
    }else if(0==strcmp("F",str)){
        if(identifier==n){
            cout<<"F->i";
            ls_td.pop_back();//弹出F
            ls_td.push_back("i");//压入i
        }else if(unsignint==n){
            cout<<"F->u";
            ls_td.pop_back();//弹出F
            ls_td.push_back("u");//压入u
        }else if(lparen==n){
            cout<<"F->(E)";
            ls_td.pop_back();//弹出F
            ls_td.push_back(")");//压入)
            ls_td.push_back("E");//压入E
            ls_td.push_back("(");//压入(
        }else{
            cout<<"无路可走"<<endl;
            psr=false;
        }
    }else if(0==strcmp("A",str)){
        if(add_sub==n && 0==strcmp(string(*(ls_str.begin())).c_str(),"+")){
            cout<<"A->'+'";
            ls_td.pop_back();//弹出A
            ls_td.push_back("+");//压入+
        }else if(add_sub==n && 0==strcmp(string(*(ls_str.begin())).c_str(),"-")){
            cout<<"A->'-'";
            ls_td.pop_back();//弹出A
            ls_td.push_back("-");//压入-
        }else{
            cout<<"无路可走"<<endl;
            psr=false;
        }
    }else if(0==strcmp("M",str)){
        if(mul_div==n && 0==strcmp(string(*(ls_str.begin())).c_str(),"*")){
            cout<<"M->'*'";
            ls_td.pop_back();//弹出M
            ls_td.push_back("*");//压入*
        }else if(mul_div==n && 0==strcmp(string(*(ls_str.begin())).c_str(),"/")){
            cout<<"M->'/'";
            ls_td.pop_back();//弹出M
            ls_td.push_back("/");//压入/
        }else{
            cout<<"无路可走"<<endl;
            psr=false;
        }
    }else{
        cout<<"无路可走"<<endl;
        psr=false;
    }
}

//传入一个字符串,返回和它意义等价的枚举值 
Status analyze(const char* str){
    if(0==strcmp("+",str) || 0==strcmp("-",str))
        return add_sub;
    else if(0==strcmp("*",str) || 0==strcmp("/",str))
        return mul_div;
    else if(0==strcmp("i",str))
        return identifier;
    else if(0==strcmp("u",str))
        return unsignint;
    else if(0==strcmp("(",str))
        return lparen;
    else if(0==strcmp(")",str))
        return rparen;
    else if(0==strcmp("#",str))
        return end;
    //全不匹配,返回的是不会被加入这个模式队列的none 
    return none;
}
//****************************************************
int main(int argc, char** argv) {
    //读入输入串并记录长度,大写字母全部变为小写 
    while('\n'!=(c=getchar()))
    {
        if('A'<=c && c<='Z')
            c=c+'a'-'A';
        s_pre[s_len++]=c;
    }
    s_pre[s_len]='\0';
    cout<<"分词情况:\n\t";
    //解析输入串并存入list,把原串的截断串也存到一个queue里 
    cutWord(ls_term,s_pre,s_len,ls_str);
    //在解析后的两个容器尾部添加一个#[结尾标识] 
    ls_term.push_back(end);
    //qu_str.push("#");
    ls_str.push_back("#");
    cout<<"输入串情况(串截断队列):\n\t";
    for(list<string>::iterator it=ls_str.begin();it!=ls_str.end();it++){
        cout<<*it<<" ";
        //如果后面改进了,可以在这里顺便做第一次的长度计算 
        //quLenSum+=string(*it).length();
    }
    cout<<endl;
/*
    while(!qu_str.empty()){
        //摧毁性验证!为了能输出看,还是用list模拟队列和栈吧 
        cout<<qu_str.front()<<" ";
        qu_str.pop();
    }
    cout<<endl;
*/
    cout<<"词法分析情况(模式队列):\n\t";
    for(list<int>::iterator it=ls_term.begin();it!=ls_term.end();it++)
        cout<<*it<<" ";
    cout<<"\n"<<endl;
    //输出表头
    cout<<"    步骤    |    分析栈    |       剩余输入串       |    推导所用产生式或匹配"<<endl; 
    ls_td.push_back("#");
    ls_td.push_back("E");
    while(true==psr && !ls_td.empty() && !ls_str.empty() && !ls_term.empty()){
        //格式输出前三列 
        printf("%7d     | ",++numNow);
        //[这里可以改进]重新计算分析栈中字符长度和 
        int stLenSum=0;//记录分析栈中字符长度和 
        for(list<string>::iterator it=ls_td.begin();it!=ls_td.end();it++){
            cout<<*it;
            stLenSum+=string(*it).length();
        }
        for(int i=stLenSum+2;i<=14;i++)
            cout<<" ";//输出第二列(分析栈)剩余的空格
        cout<<"|";
        //[这里可以改进]重新计算分析栈中字符长度和 
        int quLenSum=0;//记录剩余输入串队列中字符长度和 
        for(list<string>::iterator it=ls_str.begin();it!=ls_str.end();it++){
            quLenSum+=string(*it).length();
        }
        for(int i=quLenSum+2;i<=24;i++)
            cout<<" ";//输出第三列(输入串)前置的空格
        for(list<string>::iterator it=ls_str.begin();it!=ls_str.end();it++)
            cout<<*it;
        cout<<" | ";
        //对第四列,先尝试匹配,失败则从表(分支段)中查找产生式
        //因为输入串已经全部小写化,不必担心和非终结符重名 
        list<string>::iterator it1=ls_td.end();
        it1--;//指向模拟栈的栈顶 
        list<int>::iterator it2=ls_term.begin();//指向模式队列的头
        list<string>::iterator it3=ls_str.begin();//指向串截断队列的头 
        //终结符尝试:对这个字符串进行向枚举值的转换 
        int k=analyze(string(*it1).c_str());
        //如果和模式队列的枚举值匹配
        if(k==int(*it2)){
            //输出的是外显的串截断队列的头 
            cout<<"\""<<*it3<<"\"匹配";
            if(k!=end){
                //把两个队列同步出队(删除list头元素) 
                ls_str.erase(ls_str.begin());
                ls_term.erase(ls_term.begin());
                //模式栈栈顶弹出
                ls_td.pop_back();
            }else{
                //一方面erase不能去掉结尾元素
                //另一方面end需要特别判断
                ls_str.pop_back();
                ls_term.pop_back();
                ls_td.pop_back();
                //如果还有没空的list 
                if(!ls_td.empty() || !ls_str.empty() || !ls_term.empty()){
                    psr=false;
                }
            }
        }else{
            //语法分析:传入栈顶元素,模式队列头元素 
            ParseFunction(string(*it1).c_str(),*it2);
        }
        cout<<endl;
    }
    if(true==psr)
        cout<<"语法正确"<<endl;
    else
        cout<<"语法错误"<<endl;
    delete[] s_pre;
    return 0;
}

Rely.h

#ifndef __RELY_H__
#define __RELY_H__

#include<bits/stdc++.h>
using namespace std;

//待分析对象类型
enum Status{
    end=-2,//结尾标识,即#符号 
    none=0,//啥也不是|空格|制表符,不会被添加到list中 
    expression=1,//表达式 
    item=2,//项 
    add_sub=3,//加减
    mul_div=4,//乘除 
    factor=5,//因子
    identifier=6,//标识符
    unsignint=7,//无符号整数
    lparen=8,//左括号
    rparen=9//右括号
};

//当前字符类型 
enum Sign{
    nop=0,//没有符号|空格|制表符 
    num=7,//数字0-9
    word=6,//字母[a-z]|[A-Z]
    error=-1,//非法字符 
    //下面的符号可以直接被解析为待分析对象类型
    //所以使用和其对应的相同数值,而且名字不能相同 
    addSub=3,//加减号 
    mulDiv=4,//乘除号
    lParen=8,//左括号
    rParen=9//右括号
};

//****************************************************
//对传入的串分词,获取模式并存入list,另:存下原串的截断串 
void cutWord(list<int>& ls,char* cArry,const int& len,list<string>& ls2);
//传入一字符,获取字符类型 
Sign getSign(char c);
//****************************************************

//对传入的串分词,获取模式并存入list,另:存下原串的截断串 
void cutWord(list<int>& ls,char* cArry,const int& len,list<string>& ls2)
{
    bool isHead=true;//指示是否在扫描非终结符的头
    //不直接使用枚举类型,因为枚举类型不能转型为另一枚举类型或者int类型 
    int nowPat=none;//正在处理的模式,取决于模式中头一字符 
    int nowSign=nop;//正在处理的符号类型 
    int pre=0;//记录上一模式的开始位置 
    for(int i=0;i<len;i++)//对于数组中的每个字符 
    {
        nowSign=getSign(cArry[i]);//获取当前符号的类型 
        if(error==nowSign)//处理非法字符
        {
            cout<<"[命名错误]存在非法字符\n"<<endl;
            exit(0);
        }        
        //cout<<now<<endl;

        /*连续向下走的情况*/
        //当前在继续正常处理标识符 
        if(identifier==nowPat && (num==nowSign || word==nowSign))
            continue;
        //当前在继续正常处理无符号整数 
        if(unsignint==nowPat && num==nowSign)
            continue;

        /*命名异常立即报错*/
        //常数模式下的数字后跟字母不允许 
        if(unsignint==nowPat && word==nowSign){
            cout<<"[命名错误]纯常数数字后跟字母非法\n"<<endl;
            exit(0);
        }

        /*改变类型向下走,不作检查,词法分析阶段再处理*/
        nowPat=nowSign;//在设计枚举时设计成数字相同的
        if(none!=nowPat)//空白符不被存入list中 
        {
            //cout<<nowPat<<" ";
            ls.push_back(nowPat);//其它字符上转int被存入list中 
            //新增:存入上一模式的实际串
            if(i!=0){
                char* ext=new char[i-pre+1];//小的临时字符数组 
                for(int j=pre;j<i;j++){
                    cout<<cArry[j];
                    ext[j-pre]=cArry[j];//暂时存进去 
                }
                ext[i-pre]='\0';
                string s(ext);
                ls2.push_back(s);//存入截断串列表 
                //qu.push(s);//存入截断串队列 
                delete[] ext;
                cout<<" ";
            }
            //存好之后再把pre位置改变为当前位置 
            pre=i;
        }
    }
    //因为串总是落后一次,所以模式结束,串还有一个
    char* ext=new char[len-pre+1];//小的临时字符数组 
    for(int j=pre;j<len;j++){
        cout<<cArry[j];
        ext[j-pre]=cArry[j];//暂时存进去 
    }
    ext[len-pre]='\0';
    string s(ext);
    ls2.push_back(s);//存入截断串列表 
    //qu.push(s);//存入截断串队列 
    delete[] ext;
    cout<<" ";
    cout<<endl;
}

//传入一字符,获取字符类型 
Sign getSign(char c)
{
    if(' '==c || '\t'==c)
        return nop;//0
    else if('0'<=c && c<='9')
        return num;//1
    else if(('a'<=c && 'z'<=c)||('A'<=c && 'Z'<=c))
        return word;//2
    else if('+'==c || '-'==c)
        return addSub;//3
    else if('*'==c || '/'==c)
        return mulDiv;//4
    else if('('==c)
        return lParen;//8
    else if(')'==c)
        return rParen;//9
    return error;//5
}

#endif

运行结果

错误语法:
这里写图片描述
正确语法:
这里写图片描述

这里写图片描述

猜你喜欢

转载自blog.csdn.net/shu15121856/article/details/80209142