编译作业——LL(1)文法的语法分析器

本文介绍了一个具体的编译原理上机实验案例,详细展示了基于给定文法的预测分析过程及其实现代码。通过实例演示了如何处理不同的输入情况,并解释了代码中关键部分的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

又一次的编译上机作业,做了些修改和注释,放到博客上来

  • 注意:
    1. 以下代码的分析表是写死了的,所以仅仅只能针对文法:
E->TE'(1) 
T->FT'(2) 
E'->+TE'|e (3)
T'->*FT'|e (4)
F->(E)|id (5)
e代表空串
  • 代码可能还有漏洞,使用还请通读
package SecondHW;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

public class SecondHw {
    static String E="E",Ee="E'",T="T",Tt="T'",F="F",//代表各种非终结符
            e="e",//表示空串
            synch="synch",//同步记号,用于标记错误
            id="id";//一个终结符
    static Set<String> unLimitedSign=new HashSet<String>();
    static{//初始化 非终结符的集合
        unLimitedSign.add(E);
        unLimitedSign.add(Ee);
        unLimitedSign.add(T);
        unLimitedSign.add(Tt);
        unLimitedSign.add(F);
    }
    //创建分析预测表
    public static Map<String,Map<String,List<String>>> staticTable(){
        Map<String,Map<String,List<String>>> table=new HashMap<String, Map<String,List<String>>>();
        Map<String,List<String>> tempMap=new HashMap<String, List<String>>();
        List<String> tempList=null;
        //表的第一行
        //第一个、第二个
        tempList=fullList(new String[]{T,Ee});
        tempMap.put(id, tempList);//加入id列
        tempMap.put("(",tempList);//加入(列
        tempList=fullList(new String[]{synch});
        tempMap.put(")",tempList);
        tempMap.put("$",tempList);
        table.put(E,tempMap);//加到E行
        //表的第二行
        tempMap=new HashMap<String, List<String>>();
        tempList=fullList(new String[]{"+",T,Ee});
        tempMap.put("+", tempList);
        tempList=fullList(new String[]{e});
        tempMap.put(")",tempList);
        tempMap.put("$",tempList);
        table.put(Ee,tempMap);
        //第三行
        tempMap=new HashMap<String, List<String>>();
        tempList=fullList(new String[]{F,Tt});
        tempMap.put(id,tempList);
        tempMap.put("(",tempList);
        tempList=fullList(new String[]{synch});
        tempMap.put(")",tempList);
        tempMap.put("$",tempList);
        tempMap.put("+", tempList);
        table.put(T,tempMap);
        //第四行
        tempMap=new HashMap<String, List<String>>();
        tempList=fullList(new String[]{e});
        tempMap.put("+", tempList);
        tempMap.put(")",tempList);
        tempMap.put("$",tempList);
        tempList=fullList(new String[]{"*",F,Tt});
        tempMap.put("*",tempList);
        table.put(Tt,tempMap);
        //第五行
        tempMap=new HashMap<String, List<String>>();
        tempList=fullList(new String[]{"id"});
        tempMap.put(id,tempList);
        tempList=fullList(new String[]{"(",E,")"});
        tempMap.put("(",tempList);
        tempList=fullList(new String[]{synch});
        tempMap.put(")",tempList);
        tempMap.put("$",tempList);
        tempMap.put("+", tempList);
        tempMap.put("*", tempList);
        table.put(F,tempMap);
        return table;
    }
    //废弃方法 本来是想实现Map的级联put,简化staticTable,发现不可能,如果要写需要创建新类覆盖Map方法或者包装Map
    private static Map<String,List<String>> fullMap(String key,List<String> value){
        Map<String,List<String>> temp=new HashMap<String, List<String>>();
        temp.put(key,value);
        return temp;
    }
    /**
     * 传入一个数组,填充成一个List
     * @param String []args
     * @return List<String>
     */
    private static List<String> fullList(String[] args){
        List<String> temp=new ArrayList<String>();
        for(String s: args){
            temp.add(s);
        }
        return temp;
    }
    public static void main(String[] args){
        Map<String,Map<String,List<String>>> analysisTable=staticTable();
        LinkedList<String> stack=new LinkedList<String>();
        stack.addFirst("$");
        stack.addFirst(E);
        /*
        Scanner sc=new Scanner(System.in);
        System.out.println("输入一个记号流的串,注意每个终结符需要以空格隔开 ");
        String[] test=sc.nextLine().split(" ");
        sc.close();
        */
        //输入记号流
        //三个测试用例
        //String[] test=new String[]{id,"*",id,"+",id,"$"};
        //String[] test=new String[]{id,"*",id,"+",")",id,"$"};
        String[] test=new String[]{id,"*","+",")",id,"$"};
        List<String> temp=new ArrayList<String>();
        int point=0;
        while((!stack.getFirst().equals("$")||stack.isEmpty())&&point<test.length){//循环终止的情况+出错的情况(栈空、输入记号流指针到头了)
            if(stack.getFirst().equals(e)){//检测空串
                stack.removeFirst();
                System.out.println("弹出空串 ,栈内元素:"+stack);
            }else if(stack.getFirst().equals(test[point])){//终结符匹配,弹出 且point++
                System.out.println("匹配弹出"+stack.removeFirst()+" ,栈内元素:"+stack);
                point++;
                printString(test,point);
            }else if(!unLimitedSign.contains(stack.getFirst())){//如果是终结符,且没有在上一步处理,说明出错
                //对于右括号的特殊处理
                //针对记号流中只有左括号,没有右括号的情况,将栈中右括号弹出
                if(stack.getFirst().equals(")")){
                    System.out.println("出错1,没有匹配的括号,指出错误位置并略过");
                    error(test,point);
                    stack.removeFirst();
                }else{
                    System.out.println("出错2,不能识别的终结符,略过这一个输入记号:"+test[point]);
                    error(test,point);
                    point++;
                }
                continue;
            }else{//对非终结符进行推导+入栈
                temp=analysisTable.get(stack.getFirst()).get(test[point]);
                if(temp==null){//取得分析预测表对应记录,如果得到的是空集,代表出错了
                    System.out.println("出错3,没有匹配的产生式,略过这一个输入记号: "+test[point]);
                    error(test,point);
                    point++;
                    continue;
                }
                //处理匹配错误,这里比书上多加了对右括号的特殊处理
                //针对输入记号流只有右括号的情况,略过右括号
                //如果不将右括号单独处理,遇到右括号的同步记号,极有可能输入记号流经过若干次到e的推导,直接识别结束
                else if(temp.get(0).equals("synch")){
                    if(test[point].equals(")")){
                        System.out.println("出错4,不匹配的),略过终结符:)");
                        error(test,point);
                        point++;
                    }else{
                        System.out.println("出错5,不能识别的终结符,弹出栈顶:"+stack.removeFirst());
                        error(test,point);
                    }
                }else{
                    stack.removeFirst();
                    for(int i=temp.size()-1;i>=0;i--){
                        stack.addFirst(temp.get(i));
                    }
                    System.out.println("产生式推导,新栈内 :"+stack);
                }
            }
        }
        //这是记号流没识别完
        if(point<test.length-1){
            System.out.println("记号流输入有误,此后串不再识别");
            error(test,point);
        }
        //这是栈没弹完
        if(!stack.getFirst().equals("$")){
            System.out.println("记号流输入有误,栈中还剩元素");
            System.out.println(stack);
        }
        System.out.println("记号流已经识别完毕");
    }
    /**
     * 用于从指定位置输出一个数组的一部分
     * @param args 要输出的数组
     * @param index 指定的输出起点
     */
    public static void printString(String[] args,int index){
        System.out.print("串中还剩:");
        for(int i=index;i<args.length;i++){
            System.out.print(args[i]);
        }
        System.out.println();
    }
    /**
     * 用于展示输入记号流的出错位置
     * @param args 输入的记号流,终结符的数组
     * @param index 出错的位置
     */
    public static void error(String[] args,int index){
        for(int i=0;i<args.length;i++){
            System.out.print(args[i]+"\t");
        }
        System.out.println();
        for(int i=0;i<args.length;i++){
            if(i==index){
                System.out.print("↑这里有错");
            }else System.out.print("\t");
        }
        System.out.println();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值