LL(1)语法分析器识别句子(java)

本文介绍了一个基于LL(1)文法的语法分析程序的设计与实现过程,通过具体的例子展示了如何识别和验证特定文法的句子,并详细解释了程序的工作原理。

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

一、实验目的

设计、编制并调试一个语法分析程序,加深对语法分析原理的理解。

二、使用仪器、器材

计算机一台
操作系统:Windows10
编程软件:Intellij IDEA

三、实验内容及原理

设有文法G[E]:

E→E+T|T
T→T*F|F
F→(E)|i

消除左递归后的文法为:

E→TE’
E’→+TE’|ε
T→FT’
T’→*FT’|ε
F→(E)|i

建立LL(1)分析表如下:

I+*()#
EE→TE’E→TE’
E’E’→+TE’E’→εE’→ε
TT→FT’T→FT’
T’T’→εT’→*FT’T’→εT’→ε
FF→iF→(E)

请分析输入串i+i是否为该文法的句子,输出其分析过程

在这里插入图片描述

四、实验过程原始记录

这个类用来表示哈希表中的键,包含一个终结符和一个非终结符(也可以是#),这里要注意重写 hashCode() 和 equals() 方法。

/**
 * @Author DELL
 * @create 2020/11/26 14:26
 */
public class Pair {

    /**
     * 终结符
     */
    private String vn;

    /**
     * 非终结符
     */
    private String vt;

    public Pair(String vn, String vt) {
        this.vn = vn;
        this.vt = vt;
    }

    public String getVt() {
        return vt;
    }

    public void setVt(String vt) {
        this.vt = vt;
    }

    public String getVn() {
        return vn;
    }

    public void setVn(String vn) {
        this.vn = vn;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Pair pair = (Pair) o;
        return Objects.equals(vt, pair.vt) &&
                Objects.equals(vn, pair.vn);
    }

    @Override
    public int hashCode() {
        return Objects.hash(vt, vn);
    }

    @Override
    public String toString() {
        return "Pair{" +
                "vn='" + vn + '\'' +
                ", vt='" + vt + '\'' +
                '}';
    }
}

识别句子的一个类,核心方法为 public boolean check(String str) ;

/**
 * @Author DELL
 * @create 2020/11/26 14:26
 */
public class GrammarProcessor {

    /**
     * 分析表
     */
    public static Map<Pair,String> map = new HashMap<>();


    /**
     * vt集合,即终结符集合
     */
    public static Set<String> vtSet = new HashSet<>();

    static {
        map.put(new Pair("E","I"),"E→TE'");
        map.put(new Pair("E","("),"E→TE'");
        map.put(new Pair("E'","+"),"E'→+TE'");
        map.put(new Pair("E'",")"),"E'→ε");
        map.put(new Pair("E'","#"),"E'→ε");
        map.put(new Pair("T","I"),"T→FT'");
        map.put(new Pair("T","("),"T→FT'");
        map.put(new Pair("T'","+"),"T'→ε");
        map.put(new Pair("T'","*"),"T'→*FT'");
        map.put(new Pair("T'",")"),"T'→ε");
        map.put(new Pair("T'","#"),"T'→ε");
        map.put(new Pair("F","I"),"F→i");
        map.put(new Pair("F","("),"F→(E)");
        vtSet.add("I");
        vtSet.add("+");
        vtSet.add("(");
        vtSet.add(")");
        vtSet.add("*");
        vtSet.add("#");
    }

    public GrammarProcessor() {

    }

    /**
     * 根据一个非终结符与终结符获取产生式
     * @param vn
     * @param vt
     * @return
     */
    public static String get(String vn,String vt) {
        vt = vt.toUpperCase();
        return map.get(new Pair(vn,vt));
    }

    /**
     * 检查某个串是否符合 i+i 的语法
     * @param str
     * @return
     */
    public boolean check(String str) {
        printTitle(str);
        // 初始时 # 与 文法开始符压栈
        Deque<String> stack = new LinkedList<>();
        stack.push("#");
        stack.push("E");
        // 输入串的下一个位置
        int i = 0;
        // 输入串当前指向的字符
        String cur = String.valueOf(str.charAt(i++));
        // 栈顶符号
        String top = null;
        do{
            // 打印
            print(stack,str,i,cur);
            top = stack.pop();
            // 栈顶符号 ∈ Vt,判断栈顶与输入串当前字符是否相等
            if(vtSet.contains(top.toUpperCase())) {
                // x == a != "#",则字符串指针移动
                if(top.equalsIgnoreCase(cur)) {
                    if(!"#".equals(cur)) {
                        cur = "#";
                        System.out.println();
                        if(i < str.length()) {
                            cur = String.valueOf(str.charAt(i));
                            i++;
                        }
                    }
                }
                else {
                    return false;
                }

            } else {
                // 根据栈顶查表
                String t = get(top,cur);
                // 找不到,识别失败
                if(t == null) {
                    return false;
                }
                // 输出产生式
                System.out.println(padWhitespaceRight(t,8));
                // 获取产生式的右部
                String sentence = t.split("→")[1];
                // 将产生式的右部逆序压栈
                int end = sentence.length();
                for(int start = sentence.length()-1;start >= 0;start--) {
                    if(sentence.charAt(start) == '\'') {
                        continue;
                    } else {
                        String substring = sentence.substring(start, end);
                        // ε不压栈
                        if(!"ε".equals(substring)) {
                            stack.push(substring);
                            end = start;
                        }
                    }
                }
            }
        } while(!"#".equals(top));
        System.out.println();
        return true;
    }

    private void printTitle(String str) {
        int len = str.length() + 3;
        String[] titles = new String[] {"符号栈","当前输入符号","输入串","所用产生式"};
        for(String s:titles) {
            System.out.print(padWhitespaceRight(s,len));
        }
        System.out.println();
    }

    private void print(Deque<String> stack, String str, int i, String cur) {
        int len = str.length() + 5;
        StringBuilder sb = new StringBuilder();
        // 拼接栈内的符号
        Iterator<String> iterator = stack.descendingIterator();
        while(iterator.hasNext()) {
            sb.append(iterator.next());
        }
        // 输出栈
        System.out.print(padWhitespaceRight(sb.toString(),len));
        // 输出当前输入符号
        System.out.print(padWhitespaceRight(cur,len));
        // 输出输入串
        if(i < str.length()) {
            System.out.print(padWhitespaceRight(str.substring(i-1),len));
        } else {
            System.out.print(padWhitespaceRight("#",len));
        }
    }

    public static String padWhitespaceLeft(String s, int len) {
        return String.format("%1$" + len + "s", s);
    }

    public static String padWhitespaceRight(String s, int len) {
        return String.format("%1$-" + len + "s", s);
    }

    public static void main(String[] args) throws IOException {
        List<String> tests = new ArrayList<>();
        tests.add("i+i*i#");
        tests.add("i+i#");
        tests.add("i*i#");
        tests.add("(i+i)*i#");
        tests.add("(i*i)+(i)#");
        for(String test:tests) {
            System.out.println(new GrammarProcessor().check(test));
        }
        List<String> err = new ArrayList<>();
        err.add("i&i#");
        err.add("i/(i*i)#");
        for(String test:err) {
            System.out.println(new GrammarProcessor().check(test));
        }
    }
}

四、实验结果与分析
1.输入该文法的5个句子进行测试:

tests.add("i+i*i#");
tests.add("i+i#");
tests.add("i*i#");
tests.add("(i+i)*i#");
tests.add("(i*i)+(i)#");

测试结果分别如下:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.使用2个不是该文法的句子进行测试:

err.add("i&i#");
err.add("i/(i*i)#");

测试结果如下:
在这里插入图片描述
在这里插入图片描述

实验分析及心得
如实验结果所示,面对该文法的一些句子,该程序能够按照LL(1)文法的规则进行识别并且输出符号栈、当前输入符号、输入串、所使用产生式等信息。面对非该文法的句子,也能够正确判断不符合该文法,达到了预期目的。
该实验已经给出了LL(1)的分析表,所以免去了用程序自动生成FIRST集、FOLLOW集合、进而构造分析表的这个步骤,只需要按照分析规则自上而下判断即可。在编写程序时主要的点有:(1)产生式E’→+TE’中的E’是一个字符,所以在程序识别的时候需要稍加注意;(2)产生式的右部的压栈顺序为逆序压栈;(3)空串不用压栈。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值