2020-11-09

算符优先分析法 作业
这次题目明明很菜 但是自己还是谢了很久很久很久……放到这里 作为自己的思路总结和经验教训吧

题目 编程实现以下文法 G[E] 的算符优先分析法:

E -> E ‘+’ T | T
T -> T ‘*’ F | F
F -> ‘(’ E ‘)’ | ‘i’
输入输出要求
对于每一个测试点,你需要读入一个符合或不符合文法的句子,占据 1 行。你需要在读取这个句子之后输出使用算符优先级矩阵分析这个句子的步骤。

输入不会超过 1000 字节,且只含非空白 ASCII 字符,以 \r\n 结束。

输出要求如下:

对于每一次压栈,输出一行 I{压入的符号}(不含大括号),如 Ii 是压入 i; 对于每一次成功的、含有终结符的规约,输出一行 R (1)
; 对于失败的规约,输出一行 RE (2) ; 对于不能识别或无法比较符号优先关系的栈顶和读入符号,输出一行 E (2) 。 (1)
:因为符号优先文法不区分非终结符,如 T -> F 等的仅含非终结符的规约实际上是不存在的。

遇到 (2) 的情况,输出相应内容后不继续向下分析,直接退出。不管分析结果如何,程序返回值都应为 0。

<你的程序> 从标准输入读入。

示例输入输出 由于符号优先文法不区分非终结符,下面使用 N 代表任意非终结符。

A
输入:i

输出:

Ii
R 解释:

读入 i 规约 N <- i
B
输入:
i+i

输出:

Ii
R
I+
Ii
R
R
解释:

读入 i 规约 N <- ‘i’ 读入 + 读入 i 规约 N <- ‘i’ 规约 N <- N ‘+’ N
C
输入:
i+

输出:

Ii
R
I+
RE
D
输入:
ii

输出:

Ii
E

首先呢,说一下思路。使用java实现一个工具类一个公共类。既然是算符优先文法,肯定是要有算符优先关系矩阵的。对于这种规模的来说,编程求解反而不如手动打表输入来得快(神犇当我没说)。
实现的时候,由于一定要使用循环嵌套,而题目又要求了报错时中断,其实能够实现一个error机制代码是会更加美观和好维护的。(我偷懒没写花了不少时间在这debug)
再其次,由于opt文法要求非终结符不相邻,所以可以使用形如

  if (hash.get(stack[ptr]) != null) {
            sptr = ptr;
        } else {
            sptr = ptr - 1;
        }

的方法通过下标来简易判断。
其次,代码主要可能出错的地方在于寻找最左素短语的时候。初学者(比如我)经常会想是否需要对优先矩阵进行改动——完全不需要!
还有一点,在读入字符之后,判断他是否是表中已有的非终结符,通常能够很快的筛掉几个测试点——这也是我花了好久才发现的。
在进行规约的时候,如何保证是合法的规约?这个问题在实现的时候需要许多考虑。比方在最后按位判断的时候,判断sptr和ptr同时作为下标进行考虑就可能是一种可行的办法。
其他思路大致参照伪代码就可以了。
尽管不需要辨别非终结符,但关于这个问题的思考通常还是会引起困惑。
伪代码如下:摘自《编译原理及编译程序构造》P94
代码如下:

import java.io.File;
import java.io.FileReader;
import java.util.*;

class opt_worker
{
    static char[][] precedence=new char [][]{
        {'>','<','<','<','>','>'},
        {'>','>','<','<','>','>'},
        {'>','>','u','u','>','>'},
        {'<','<','<','<','=','u'},
        {'>','>','u','u','>','>'},
        {'<','<','<','<','u','='},
        };
    int ptr=0;
    char[] stack=new char[10000000];
    Map <Character,Integer> hash= new HashMap <Character,Integer>();
    void init ()
    {
        hash.put('+',Integer.valueOf(0));
        hash.put('*',Integer.valueOf(1));
        hash.put('i',Integer.valueOf(2));
        hash.put('(',Integer.valueOf(3));
        hash.put(')',Integer.valueOf(4));
        hash.put('#',Integer.valueOf(5));
        stack[ptr]='#';
    }
    void work(char[] buf)
    {
        char ch;
        int sptr;//扫描指针
        boolean error_flag=false;
        for(int i=0;i<buf.length;i++)
        {
            ch=buf[i];
            if(error_flag)
            {
                break;
            }
            if(buf[i]=='\r')
            {
                ch='#';
                error_flag=true;
            }

            if(hash.get(stack[ptr])!=null)
            {
                sptr=ptr;
            }
            else
            {
                sptr=ptr-1;
            }
            if(hash.get(ch)==null)
            {
                System.out.println("E");
                error_flag=true;
                break;
            }
            if(precedence[hash.get(stack[sptr])][hash.get(ch)]=='>')//试图规约
            {
                while(precedence[hash.get(stack[sptr])][hash.get(ch)]=='>')
                {
                    char Q=stack[sptr];
                    while(!(precedence[hash.get(stack[sptr])][hash.get(Q)]=='<'))
                    {

                        Q=stack[sptr];
                        if(hash.get(stack[sptr-1])!=null)
                        sptr-=1;
                        else 
                        sptr-=2;

                    }
                    if((sptr+1==ptr&&stack[sptr+1]=='i')||
                    (stack[sptr+1]=='N'&&stack[sptr+2]=='+'&&stack[ptr]=='N')||
                    (stack[sptr+1]=='N'&&stack[sptr+2]=='*'&&stack[ptr]=='N')||
                    (stack[sptr+1]=='('&&stack[sptr+2]=='N'&&stack[ptr]==')'))                  
                    {
                        ptr=sptr+1;
                        stack[ptr]='N';
                        System.out.println('R');
                        if(precedence[hash.get(stack[sptr])][hash.get(ch)]=='u')
                        {
                            System.out.println("E");
                            error_flag=true;
                            break;
                        }
                    }
                    else
                    {
                        System.out.println("RE");
                        error_flag=true;
                        break;
                    }

                }
            }
            if(error_flag)
            {
                break;
            }
            if(precedence[hash.get(stack[sptr])][hash.get(ch)]=='<'||precedence[hash.get(stack[sptr])][hash.get(ch)]=='=')//小于或等于则移进
            {
                if(ch=='#')
                {
                    break;
                }
                stack[++ptr]=ch;
                System.out.println("I"+ch);
            }
            else if (precedence[hash.get(stack[sptr])][hash.get(ch)]=='u')//未定义
            {
                System.out.println("E");
                error_flag=true;
                break;
            }
            else
            {
                System.out.println('E');
                error_flag=true;
                break;
            }

        }
    }
}
public class opt_analyser
{
    public static void main (String[] args)throws Exception
    {
        File file= new File(args[0]);
        FileReader reader=new FileReader(file);
        int length=(int)file.length();
        char buf[]=new char[length+1];
        reader.read(buf);
        reader.close();
        opt_worker now= new opt_worker();
        now.init();
        now.work(buf);
    }
     
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值