编译原理-算符优先

/*
*功能:算符优先
*作者:王文堃
*创建时间:2016/5/15
*/

#include <iostream>
#include <malloc.h>
#include <stack>
using namespace std;

/*
*任务一:构造FIRSTVT,LASTVT
*任务开始时间:2016/5/15
*任务结束时间:2016/5/16
*/

/*
*任务二:构造算符优先表
*任务开始时间:2016/5/17
*任务完成时间:2016/5/17
*/

/*
*任务三:根据输入判断结果
*任务开始时间:2016/5/17
*任务完成时间:2016/5/17
*/
#define MAXSIZE 10
#define NUM_VT 7
#define NUM_VN 5

char WenFa[MAXSIZE][MAXSIZE]; //存放从文件中读取的文法
int g_num_wenfa = 0; //存放文法的个数,初值为0
char vt[NUM_VT] = {‘+‘,‘*‘,‘^‘,‘i‘,‘(‘,‘)‘,‘#‘}; //所有终结符
char vn[NUM_VN] = {‘E‘,‘T‘,‘F‘,‘P‘,‘S‘}; //存放所有非终结符
int firstvt[NUM_VN][NUM_VT]; //存放每一个非终结符的firstvt
int lastvt[NUM_VN][NUM_VT]; //存放每一个非终结符的lastvt
int labelofch[MAXSIZE]; //存放已ch打头的所有文法的标号
char OPT[NUM_VT][NUM_VT];  //算符优先表

char Input[MAXSIZE][MAXSIZE]; //保存输入
int g_num_input = 0; //存放输入的个数
stack <char> OPTR; //符号栈
stack <char> OPND; //数据栈

/*-----------------------------------------计算FIRSTVT----------------------------------------*/

//读取文法
bool readfromfile(char* str)
{
    FILE* fp = fopen(str, "r");
    if (fp) //succeed
    {
        while (EOF != fscanf(fp, "%s", WenFa[g_num_wenfa]))
        {
            g_num_wenfa++;
        }
        fclose(fp);
        return true;
    }
    else
    {
        printf("error to open the file\n");
        return false;
    }
}

//判断文法中字符是否为终结符
int isVt(char ch)
{
    for (int i = 0; i < NUM_VT; i++)
    {
        if (ch == vt[i])
        {
            return true;
        }
    }
    return false;
}

//判断文法中字符是否为非终结符
int isVn(char ch)
{
    for (int i = 0; i < NUM_VN; i++)
    {
        if (ch == vn[i])
        {
            return true;
        }
    }
    return false;
}

//找到以ch开头的文法的下标
int findit(char ch)
{
    int num = 0;
    for (int i = 0; i < g_num_wenfa; i++)
    {
        if (WenFa[i][0] == ch)
            labelofch[num++] = i;
    }
    return num; //返回以ch开头文法的个数
}

//将非终结符转换成下标
int Vn2num(char ch)
{
    switch (ch)
    {
    case ‘E‘:
        return 0;
        break;
    case ‘T‘:
        return 1;
        break;
    case ‘F‘:
        return 2;
        break;
    case ‘P‘:
        return 3;
        break;
    case ‘S‘:
        return 4;
        break;
    default:
        return -1;
    }
}

//将终结符转换成下标
int Vt2num(char ch)
{
    switch (ch)
    {
    case ‘+‘:
        return 0;
        break;
    case ‘*‘:
        return 1;
        break;
    case ‘^‘:
        return 2;
        break;
    case ‘i‘:
        return 3;
        break;
    case ‘(‘:
        return 4;
        break;
    case ‘)‘:
        return 5;
        break;
    case ‘#‘:
        return 6;
        break;
    default:
        return -1;
    }
}

//求FIRSTVT(ch)
void get_firstvt(char ch)
{
    if (isVn(ch)) //对终非结符ch
    {
        int firstnum = Vn2num(ch); //ch所对应的编号
        int num = findit(ch); //找到以该非终结符开头文法的下标
        for (int i = 0; i < num; i++)
        {
            char rightfirst = WenFa[labelofch[i]][3]; //取出文法推到的第一个字符
            if (isVt(rightfirst)) //该文法推出的第一个字符是终结符
            {
                firstvt[firstnum][Vt2num(rightfirst)] = 1;
            }
            else //该文法推出的第一个字符是非终结符
            {
                char rightsc = WenFa[labelofch[i]][4];
                if (isVt(rightsc)) //判断该非终结符之后的符号是不是终结符
                {
                    firstvt[firstnum][Vt2num(rightsc)] = 1;
                }

                if (ch != rightfirst)
                {
                    get_firstvt(rightfirst); //计算这个非终结符的firstvt集
                    int newvn = Vn2num(rightfirst);
                    for (int j = 0; j < NUM_VT; j++)
                    {
                        if (firstvt[newvn][j] == 1)
                        {
                            firstvt[firstnum][j] = 1;
                        }
                    }
                }
            }
        }

    }
}

//计算所有firstvt
void do_firstvt()
{
    for (int i = 0; i < NUM_VN; i++)
    {
        get_firstvt(vn[i]);
    }

}

//输出firstvt表
void output_firstvt()
{
    int i = 0;
    printf("---------------firstvt----------------\n");
    printf(" ");
    for (i = 0; i < NUM_VT; i++)
    {
        printf("%5c", vt[i]);
    }
    printf("\n");
    for (i = 0; i < NUM_VN; i++)
    {
        printf("%c", vn[i]);
        for (int j = 0; j < NUM_VT; j++)
        {
            printf("%5d", firstvt[i][j]);
        }
        printf("\n");
    }
    printf("--------------------------------------\n");
    printf("\n");
}

/*-----------------------------------------计算LASTVT----------------------------------------*/
//找到下标为num的文法的最后一个字符的下标
int get_last(int num)
{
    for (int i = 0; i < MAXSIZE; i++)
    {
        if (WenFa[num][i] == ‘\0‘)
        {
            return (i-1);
        }
    }
}

//求LASTVT(ch)
void get_lastvt(char ch)
{
    if (isVn(ch)) //对终非结符ch
    {
        int lastnum = Vn2num(ch); //ch所对应的编号
        int num = findit(ch); //找到以该非终结符开头文法的下标
        for (int i = 0; i < num; i++)
        {
            int ilast = get_last(labelofch[i]);
            char rightlast = WenFa[labelofch[i]][ilast]; //获得文法的最后一个字符
            if (isVt(rightlast)) //该文法推出的最后一个字符是终结符
            {
                lastvt[lastnum][Vt2num(rightlast)] = 1;
            }
            else //该文法推出的最后一个字符是非终结符
            {
                char rightlastsc = WenFa[labelofch[i]][ilast -1]; //文法倒数第二个字符
                if (isVt(rightlastsc)) //判断倒数第二个字符是不是终结符
                {
                    lastvt[lastnum][Vt2num(rightlastsc)] = 1;
                }

                if (ch != rightlast)
                {
                    get_lastvt(rightlast); //计算这个非终结符的lastvt集
                    int newvn = Vn2num(rightlast);
                    for (int j = 0; j < NUM_VT; j++)
                    {
                        if (lastvt[newvn][j] == 1)
                        {
                            lastvt[lastnum][j] = 1;
                        }
                    }
                }
            }
        }

    }
}

//计算所有lastvt
void do_lastvt()
{
    for (int i = 0; i < NUM_VN; i++)
    {
        get_lastvt(vn[i]);
    }
}

//输出lastvt表
void output_lastvt()
{
    int i = 0;
    printf("----------------lastvt----------------\n");
    printf(" ");
    for (i = 0; i < NUM_VT; i++)
    {
        printf("%5c", vt[i]);
    }
    printf("\n");
    for (i = 0; i < NUM_VN; i++)
    {
        printf("%c", vn[i]);
        for (int j = 0; j < NUM_VT; j++)
        {
            printf("%5d", lastvt[i][j]);
        }
        printf("\n");
    }
    printf("--------------------------------------\n");
    printf("\n");
}

/*----------------------------------------构造算符优先表---------------------------------------*/
//处理小于的情况,a<FIRSTVT(R)
void do_less(char vtch1, char vnch2)
{
    int x = Vt2num(vtch1); //a
    int i = Vn2num(vnch2); //firstvt表中的行
    for (int j = 0; j < NUM_VT; j++) //遍历firstvt表
    {
        if (firstvt[i][j] == 1)
            OPT[x][j] = ‘<‘;
    }
}

//处理大于的情况,LASTVT(R)>a
void do_more(char vnch1, char vtch2)
{
    int y = Vt2num(vtch2);
    int i = Vn2num(vnch1);
    for (int j = 0; j < NUM_VT; j++) //遍历LASTVT表
    {
        if (lastvt[i][j] == 1)
        {
            OPT[j][y] = ‘>‘;
        }
    }
}

//处理#号
void dosharp()
{
    //添加“#=#”
    int temp = Vt2num(‘#‘);
    OPT[temp][temp] = ‘=‘;
    //添加‘#‘<FIRSTVT(S)、LASTVT(S)>‘#‘
    for (int i = 0; i < NUM_VT; i++)
    {
        int x = Vn2num(‘S‘);
        int j = Vt2num(‘#‘);
        if (firstvt[x][i] == 1)
        {
            OPT[j][i] = ‘<‘;
        }
        if (lastvt[x][i] == 1)
        {
            OPT[i][j] = ‘>‘;
        }
    }
}

//构造算法优先表
void OperatorPrecedence()
{
    int i = 0; //循环变量
    for (i = 0; i < g_num_wenfa; i++) //对所有的文法
    {
        int j = 3; //内部循环变量,从推出来的第一个字符开始

        while (WenFa[i][j] != ‘\0‘) //该文法没有完
        {
            char ch = WenFa[i][j];
            if (isVt(ch)) //判断当前字符是不是终结符
            {
                //判断下一个字符是不是终结符
                if (isVt(WenFa[i][j + 1]))
                {
                    //若是,则这两个终结符的优先级相等
                    OPT[Vt2num(ch)][Vt2num(WenFa[i][j + 1])] = ‘=‘;
                }
                else
                {
                    //做小于
                    do_less(ch, WenFa[i][j + 1]);

                    //继续判断下一个字符是不是终结符
                    if (isVt(WenFa[i][j + 2]))
                    {
                        //若是,则这两个终结符的优先级依然相等
                        OPT[Vt2num(ch)][Vt2num(WenFa[i][j + 2])] = ‘=‘;
                    }
                }
            }
            else //当前是非终结符
            {
                if (isVt(WenFa[i][j + 1])) //判断下一个字符
                {
                    //大于
                    do_more(ch, WenFa[i][j + 1]);
                }
            }
            j++;
        }
    }
    dosharp(); //处理#
}

//输出算符优先表
void output_OPT()
{
    int i = 0;
    printf("------------------OPT-----------------\n");
    printf(" ");
    for (i = 0; i < NUM_VT; i++)
    {
        printf("%5c", vt[i]);
    }
    printf("\n");
    for (i = 0; i < NUM_VT; i++)
    {
        printf("%c", vt[i]);
        for (int j = 0; j < NUM_VT; j++)
        {
            printf("%5c", OPT[i][j]);
        }
        printf("\n");
    }
    printf("--------------------------------------\n");
    printf("\n");
}

/*---------------------------------------处理输入判断结果---------------------------------------*/
//从文件中读取输入
bool inputformfile(char* str)
{
    FILE* fp = fopen(str, "r");
    if (fp) //succeed
    {
        while (EOF != fscanf(fp, "%s", Input[g_num_input]))
        {
            g_num_input++;
        }
        fclose(fp);
        return true;
    }
    else
    {
        printf("error to open the file\n");
        return false;
    }
}

//判断输入字符的类型
int judge(char ch)
{
    switch (ch)
    {
    case ‘i‘:
        return 0; //表示数字
        break;
    case ‘+‘:
        return 1; //表示‘+‘号
        break;
    case ‘*‘:
        return 2; //表示‘*‘号
        break;
    default:
        return -1;
    }
}

//将int行数字传化成char型
int char2int(char ch)
{
    if (ch >= 48 && ch <= 57) //数字
    {
        return ch - 48;
    }
}

//处理输入
bool doinput()
{
    int iInput = 0; //指向输入下标
    //#入符号栈
    OPTR.push(‘#‘);
    while (iInput != (g_num_input-1)  || OPTR.size() != 0) //将输入遍历完
    {
        char* str = Input[iInput]; //取出第一个输入
        int result = judge(str[1]);
        if (result == 0) //数字
        {
            OPND.push(str[3]);
        }
        else //是符号
        {
            //判断符号栈顶的符号和当前符号的优先级
            int topofstack = Vt2num(OPTR.top());
            int topofinput = Vt2num(str[1]);
            if (OPT[topofstack][topofinput] == ‘<‘) //栈顶小于输入就入栈
            {
                OPTR.push(str[1]);
            }
            else if (OPT[topofstack][topofinput] == ‘>‘)//否则运算
            {
                int i = char2int(OPND.top());
                OPND.pop();
                int j = char2int(OPND.top());
                OPND.pop();

                int vtnum = Vt2num(OPTR.top());
                if (vtnum == 0) //+
                {
                    OPND.push(i + j);
                    OPTR.pop();
                }
                else if (vtnum == 1) //*
                {
                    OPND.push(i*j);
                    OPTR.pop();
                }
            }
            else //如果相等就出栈
            {
                OPTR.pop();
            }
        }
        if (iInput != (g_num_input-1))
        {
            iInput++;
        }
    }

    //判断
    if (OPTR.size() == 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

int main()
{
    if (readfromfile("nick.txt"))
    {
        do_firstvt();  //构造firstvt
        output_firstvt(); //输出firstvt
        do_lastvt();  //构造lastvt
        output_lastvt(); //输出lastvt
        OperatorPrecedence(); //构造算符优先表
        output_OPT(); //输出算符优先表

        //---
        inputformfile("input.txt");
        bool result = doinput();
        if (result != 0)
        {
            printf("最终结果正确\n");
        }
        else
        {
            printf("最终结果错误\n");
        }

    }
    getchar();
    return 0;
}

两个文件的内容分别是:

1.nick.txt

S->E
E->E+T
E->T
T->T*F
T->F
F->P^F
F->P
P->(E)
P->i

2.input.txt

(i,1)
(+,)
(i,2)
(*,)
(i,3)
(#,)

最终的运算结果是:

时间: 2024-10-12 11:49:54

编译原理-算符优先的相关文章

编译原理——算符优先分析文法(附源代码)

算符优先分析文法 一.写在前面 算符优先分析文法是一种工具,在编译的过程中,隶属于语法分析环节,却又与中间代码的生成息息相关,编译可以分为五个阶段:词法分析.语法分析.语义分析(中间代码的生成).代码优化.目标代码生成.语法分析是指:在词法分析基础上,将单词符号串转化为语法单位(语法范畴)(短语.子句.句子.程序段.程序),并确定整个输入串是否构成语法上正确的程序.也就是说语法分析是检验输入串的语法是否正确,注意这里的语法正确,只是简单地符合自己定义的规范,而不能检测出运行时错误,比如"X/0&

编译原理之算符优先分析

1. 已知算符优先关系矩阵如下表:   + * i ( ) # + > < < < > > * > > < < > > i > >     > > ( < < < < =   ) > >     > > # < < < <   =  写出符号串(i+i)*i#的算符优先分析过程. 2.接上个文章两个步骤. 1)计算FIRSTVT和 LAST

编译原理基础概念介绍

关于编译原理 语法树 句柄 简单短语 短语 的区分,通过两个例子来理解概念以及方法: 例子1——语法树 S -> a|b|(T)  T -> TdS|S Vt={a,b,d,(,)}.Vn={S,T},S是开始符 句型(Sd(T)db)是S的一个推导,其中___是句柄;____是最左素短语:____是该句型的直接短语,_____是短语.     素短语的概念:它是一个递归的定义,至少含有一个终结符,并且除它自身之外不再含任何更小的素短语,所谓最左素短语就是处于句型最左边的素短语的短语.而一个算

了解编译原理-笔记小结

这是之前学习编译原理过程中做下的笔记. 因能力有限,在很多地方都理解不到位,特别是对于词法分析与语法分析的过程感觉特别晦涩. 分享这个笔记也是为了自己做个总结,算是一个小的提纲吧,都没怎么深入解析编译的过程. 等以后领悟更多了再作补充吧. 希望各路人士能多加指点,谢谢. 词法分析 作用:将输入转换为一个一个的token,而其用一串整数来表示. 协作:只有当解析器需要的时候才会请求词法分析器,继续扫描输入流,在这个过程中将不断生成符号表. 实现:在通常的编程语言中,相对于不确定的有限自动机(NFA

编译原理课程设计——语法分析器

实验目的 了解掌握算符优先分析的基本方法.内容:学会科学思考并解决问题,提高程序设计能力. 实验内容与要求 用算符优先分析方法设计一个分析解释程序,对输入的赋值语句.输出语句.清除语句进行词法分析.语法分析.表达式求值并存储于指定变量中:若存在错误,提示错误相关信息. 文法表示 S -> v=E | E? | clear E -> E+T | E–T | T T -> T*F | T/F | F F -> (E) | v | c 问题分析 由于要求用算符优先分析方法来进行程序的语法

编译原理 实验3 语法分析

语法分析 一. 实验目的 算术表达式的文法可以是(你可以根据需要适当改变): E→E+E|E-E|E*E|E/E|(E)|i 根据算符优先分析法,将表达式进行语法分析,判断一个表达式是否正确. 二. 实验环境 操作系统:window xp 编写环境:visual c++ 编写语言:c语言 三. 实验内容 程序输入/输出示例: 如参考C语言的运算符.输入如下表达式(以分号为结束)和输出结果: (1)10; 输出:正确 (2)1+2; 输出:正确 (3)(1+2)/3+4-(5+6/7); 输出:正

编译原理三大经典书籍(龙书 虎书 鲸书)

1.龙书(Dragon book)  英文名:Compilers: Principles,Techniques,and Tools  作者:Alfred V.Aho,Ravi Sethi,Jeffrey D.Ullman  中文名:编译原理技术和工具   第一版龙书   第二版龙书 龙书”.龙书是Alfred V. Aho等人于1986年出版的,由于出版年代较早,其中包含部分过时的技术并且没有反映一些新的编译技术.新编的<编译原理>抛弃诸如算符优先分析等过时技术,增加面向对象编译.类型检查等新

软考-程序设计语言基础(编译原理)

首先声明一下,本系列软考的文章是针对软件设计师(中级)的. 在软件设计师考试中,关于程序设计语言这一章节,前面的知识很基础,像一些控制结构和数据类型的知识我想大家都非常熟练就没有总结在图里. 本章节的重点内容在于编译原理,编译原理指的是编译器是将汇编或高级计算机语言翻译为二进制机器语言代码的计算机程序.内容主要包括文法.正规式.有限自动机.语法推导树. 好了,不多说,还是老规矩用图来介绍. 重点看一下编译原理,展开前三项看看. 文法,是描述语法结构的形式规则: 正规式是描述程序语言单词的表达式,

实况:《编译原理》期末考试一天复习

大学的编译原理课程是讲解如何设计编译器的.我要做的,是在一学期没怎么听课的条件下,用我对算法的理解能力,和互联网以及身边同学的帮助,来在不足一天的时间中复习完考试所需的所有知识. 我手上有两份可用资料: 复习课上记录的,老师对考试知识点的回顾. 同学总结的,会考的算法的列表. 而现在的时间是晚上6:33,我刚刚看完大约四分之一,明天下午1:10就考试了.由于时间紧迫,我必须对自己的任务有个计划,不能浪费一点时间.这个计划会花去我大概半个小时的时间,但它是一定要做的,否则我一定复习不完. 考试涉及