LL1文法_预测分析法_语法分析器

设计要求:对于任意输入的一个LL(1)文法,构造其预测分析表,并对指定输入串分析其是否为该文法的句子。
思路:首先实现集合FIRST(X)构造算法和集合FOLLOW(A)构造算法,再根据FIRST和FOLLOW集合构造出预测分析表,并对指定的句子打印出分析栈的分析过程,判断是否为该文法的句子。

  1. 求FIRST集的算法思想
    如果产生式右部第一个字符为终结符,则将其计入左部first集
    如果产生式右部第一个字符为非终结符
    ->求该非终结符的first集
    ->将该非终结符的非$first集计入左部的first集
    ->若存在$,则将指向产生式的指针右移
    ->若不存在$,则停止遍历该产生式,进入下一个产生式
    ->若已经到达产生式的最右部的非终结符,则将$加入左部的first集
    处理数组中重复的first集中的终结符

  2. 求FOLLOW集的算法思想
    对于文法G中每个非终结符A构造FOLLOW(A)的办法是,连续使用下面的规则,直到每个FOLLOW不在增大为止.
    (1) 对于文法的开始符号S,置#于FOLLOW(S)中;
    (2) 若A->aBb是一个产生式,则把FIRST(b){ε}加至FOLLOW(B)中;
    (3) 若A->aB是一个产生式,或A->aBb是一个产生式而b=>ε(即ε∈FIRST(b))则把FOLLOW(A)加至FOLLOW(B)中

  3. 生成预测分析表的算法思想
    构造分析表M的算法是:
    (1) 对文法G的每个产生式A->a执行第二步和第三步;
    (2) 对每个终结符a∈FIRST(a),把A->a加至M[A,a]中;
    (3) 若ε∈FIRST(a),则把任何b∈FOLLOW(A)把A->a加至M[A,b]中;
    (4) 把所有无定义的M[A,a]标上出错标志.

  4. 对符号串的分析过程
    预测分析程序的总控程序在任何时候都是按STACK栈顶符号X和当前的输入符号行事的,对于任何(X,a),总控程序
    每次都执行下述三种可能的动作之一;
    (1) 若X=a=”#”,则宣布分析成功,停止分析过程.
    (2) 若X=a≠”#”,则把X从STACK栈顶逐出,让a指向下一个输入符号.
    (3) 若X是一个非终结符,则查看分析表M,若M[A,a]中存放着关于X的一个产生式,那么,首先把X逐出STACK栈顶,然后
    把产生式的右部符号串按反序一一推进STACK栈(若右部符号为ε,则意味着不推什么东西进栈).在把产生式的右部
    符号推进栈的同时应做这个产生式相应得语义动作,若M[A,a]中存放着”出错标志”,则调用出错诊察程序ERROR.
    句子采用i*i+i
    文法采用课本P69消除左递归后的4.2文法:

E->TE'
E'->+TE'|ε
T->FT'
T'->*FT'|ε
F->(E)|i

LL1_Deque.java源码如下:

package com.LL1;
import java.util.ArrayDeque;
import java.util.Deque;

/**
 * LL1文法分析器,已经构建好预测分析表,采用Deque实现
 * Created by HongWeiPC on 2017/5/12.
 */
public class LL1_Deque {
    //预测分析表
    private String[][] analysisTable = new String[][]{
            {"TE'", "", "", "TE'", "", ""},
            {"", "+TE'", "", "", "ε", "ε"},
            {"FT'", "", "", "FT'", "", ""},
            {"", "ε", "*FT'", "", "ε", "ε"},
            {"i", "", "", "(E)", "", ""}
    };

    //终结符
    private String[] VT = new String[]{"i", "+", "*", "(", ")", "#"};

    //非终结符
    private String[] VN = new String[]{"E", "E'", "T", "T'", "F"};

    //输入串strToken
    private StringBuilder strToken = new StringBuilder("i*i+i");

    //分析栈stack
    private Deque<String> stack = new ArrayDeque<>();

    //shuru1保存从输入串中读取的一个输入符号,当前符号
    private String shuru1 = null;

    //X中保存stack栈顶符号
    private String X = null;

    //flag标志预测分析是否成功
    private boolean flag = true;

    //记录输入串中当前字符的位置
    private int cur = 0;

    //记录步数
    private int count = 0;

    public static void main(String[] args) {
        LL1_Deque ll1 = new LL1_Deque();
        ll1.init();
        ll1.totalControlProgram();
        ll1.printf();
    }

    //初始化
    private void init() {
        strToken.append("#");
        stack.push("#");
        System.out.printf("%-8s %-18s %-17s %s\n", "步骤 ", "符号栈 ", "输入串 ", "所用产生式 ");
        stack.push("E");
        curCharacter();
        System.out.printf("%-10d %-20s %-20s\n", count, stack.toString(), strToken.substring(cur, strToken.length()));
    }

    //读取当前栈顶符号
    private void stackPeek() {
        X = stack.peekFirst();
    }

    //返回输入串中当前位置的字母
    private String curCharacter() {
        shuru1 = String.valueOf(strToken.charAt(cur));
        return shuru1;
    }

    //判断X是否是终结符
    private boolean XisVT() {
        for (int i = 0; i < (VT.length - 1); i++) {
            if (VT[i].equals(X)) {
                return true;
            }
        }
        return false;
    }

    //查找X在非终结符中分析表中的横坐标
    private String VNTI() {
        int Ni = 0, Tj = 0;
        for (int i = 0; i < VN.length; i++) {
            if (VN[i].equals(X)) {
                Ni = i;
            }
        }
        for (int j = 0; j < VT.length; j++) {
            if (VT[j].equals(shuru1)) {
                Tj = j;
            }
        }
        return analysisTable[Ni][Tj];
    }

    //判断M[A,a]={X->X1X2...Xk}
    //把X1X2...Xk推进栈
    //X1X2...Xk=ε,不推什么进栈
    private boolean productionType() {
        return VNTI() != "";
    }

    //推进stack栈
    private void pushStack() {
        stack.pop();
        String M = VNTI();
        String ch;
        //处理TE' FT' *FT'特殊情况
        switch (M) {
            case "TE'":
                stack.push("E'");
                stack.push("T");
                break;
            case "FT'":
                stack.push("T'");
                stack.push("F");
                break;
            case "*FT'":
                stack.push("T'");
                stack.push("F");
                stack.push("*");
                break;
            case "+TE'":
                stack.push("E'");
                stack.push("T");
                stack.push("+");
                break;
            default:
                for (int i = (M.length() - 1); i >= 0; i--) {
                    ch = String.valueOf(M.charAt(i));
                    stack.push(ch);
                }
                break;
        }
        System.out.printf("%-10d %-20s %-20s %s->%s\n", (++count), stack.toString(), strToken.substring(cur, strToken.length()), X, M);
    }

    //总控程序
    private void totalControlProgram() {
        while (flag) {
            stackPeek();  //读取当前栈顶符号  令X=栈顶符号
            if (XisVT()) {
                if (X.equals(shuru1)) {
                    cur++;
                    shuru1 = curCharacter();
                    stack.pop();
                    System.out.printf("%-10d %-20s %-20s \n", (++count), stack.toString(), strToken.substring(cur, strToken.length()));
                } else {
                    ERROR();
                }
            } else if (X.equals("#")) {
                if (X.equals(shuru1)) {
                    flag = false;
                } else {
                    ERROR();
                }
            } else if (productionType()) {

                if (VNTI().equals("")) {
                    ERROR();
                } else if (VNTI().equals("ε")) {
                    stack.pop();
                    System.out.printf("%-10d %-20s %-20s %s->%s\n", (++count), stack.toString(), strToken.substring(cur, strToken.length()), X, VNTI());
                } else {
                    pushStack();
                }
            } else {
                ERROR();
            }
        }
    }

    //出现错误
    private void ERROR() {
        System.out.println("输入串出现错误,无法进行分析");
        System.exit(0);
    }

    //打印存储分析表
    private void printf() {
        if (!flag) {
            System.out.println("****分析成功啦!****");
        } else {
            System.out.println("****分析失败了****");
        }
    }
}

运行结果如下图所示


运行结果
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,552评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,666评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,519评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,180评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,205评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,344评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,781评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,449评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,635评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,467评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,515评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,217评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,775评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,851评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,084评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,637评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,204评论 2 341

推荐阅读更多精彩内容