zoukankan      html  css  js  c++  java
  • LR(1)语法分析器生成器(生成Action表和Goto表)java实现(二)

      updata : 附我之前bilibili讲解视频链接 : https://www.bilibili.com/video/av63666423?share_medium=android&share_source=qq&bbid=PQ0-BzIEPAU2VGNXK1crinfoc&ts=1565782566880

      目前完成进度 : 目前已经完成了表驱动,通过函数输出这个Action 和 Goto表。然后使用者就可以根据两个表来进行LR(1)语法分析。且经过比对,发现和书上的例子(括号语法)是完全吻合的。

      

       1 package cn.vizdl.LR1.version3;
       2 
       3 import java.util.ArrayList;
       4 import java.util.HashMap;
       5 import java.util.HashSet;
       6 import java.util.List;
       7 import java.util.Scanner;
       8 import java.util.Set;
       9 
      10 /*
      11 项目名 :  LR(1) parser generator (LR(1)语法分析器生成器)
      12 项目分析 : 
      13     输入 : 输入某文件内存地址。且内部采用  <Expr> ::=  <Term> + <Factor>; 的结构输入的LR(1)语法。
      14     这里的仅支持 BNF范式内部的 终结符,非终结符,或运算,;(表示产生式结束),::=(表示为定义为)
      15     在这里不支持闭包,也就是{},因为闭包可以转换为非终结符的递归。
      16         输入文本格式 : 要求输入语法为 无回溯语法,在前瞻一个符号的情况下,总能预测正确的产生式规则。
      17             start : <aim_name>  //aim_name表示起始符号名称 
      18         例子 : 
      19         //这是错误的例子,不符合LR(1)语法
      20             start : <Goal>;
      21             <Goal> ::= <Expr>;
      22             <Expr> ::= <Expr> "+" <Term> | <Expr> "-" <Term>;
      23             <Term> ::= <Term> "*" <Factor> | <Term> "/" <Factor>;
      24             <Factor> ::= "number";
      25             #
      26             
      27             
      28             start : <Goal>;
      29             <Goal> ::= <Expr>;
      30             <Expr> ::= <Term><Expr'>;
      31             <Expr'> ::= "+" <Term><Expr'> 
      32                     |    "-" <Term><Expr'> 
      33                     |    "ε";
      34             <Term> ::= <Factor><Term'>;
      35             <Term'> ::= "*" <Factor><Term'>
      36                     |    "/" <Factor><Term'>
      37                     |    "ε";
      38             <Factor> ::= "("<Expr>")"
      39                     |    "num"
      40                     |    "name";
      41             #
      42             
      43             
      44             start : <Goal>;
      45             <Goal> ::= <List>;
      46             <List> ::= <List><Pair>
      47                     |    <Pair>;
      48             <Pair> ::= "(" <Pair> ")"
      49                     |    "("")";
      50             #
      51         以#作为结尾
      52     输入分析 : 因为上下文无关语法是一个四元组,而LR(1)语法又是上下文无关语法的子集。所以采用四元组的形式来表示LR(1)语法,是不会损失信息的。
      53         四元组 (T,NT,S,P)
      54         T : 终结符集合
      55         NT : 非终结符集合
      56         S : 语法的起始符号(非终结符)
      57         P : 产生式集合 
      58         T, NT都可以用一个hash_set来表示。
      59         P 可以分为两个部分,左侧一定是一个非终结符,右侧是一个支持或运算的产生式。 
      60         产生式左端可以使用Node节点来表示,产生式右端可以使用多个链表(具体有几个取决于当前产生式有多少个或运算符)来表示。
      61         将当下语法分为三级,第一级是Expr,第二级别是Term,第三个级别是Factor
      62         <Expr> ::= <Term> { "|" <Term>}; //产生式(表达式)可以表达成多个小句子 或 起来
      63         <Term> ::= <Factor> { "+" <Factor>}; // + 表示连接
      64         <Factor> ::= <T> | <NT>
      65     输出 : Action 和 GoTo 表。 
      66     
      67     1.0完成进度 : 完成了将 输入字符串 转换成了 中间数据结构(BnfContainer)表示。
      68     2.0完成进度 : closure()闭包函数
      69     在这里 [A -> β·Cθ,a]指的是,识别完了A后非终结符号为a(也就是LR(1)中的1,前瞻一个符号)。
      70     关于FIRST : 
      71         FIRST(A) : 对于语法符号A,FIRST(A)表示,从A推导出的符号串的第一个单词所对应的终结符的集合。
      72         FIRST定义域 : T ∪ NT ∪ {ε, eof}
      73         FIRST值域 : T ∪ {ε, eof}
      74         如若A等于T ∪ {ε, eof}
      75         那么 FIRST(A) = A
      76     在闭包函数中,难点在于 FIRST(θa),这不是一个简单的 FIRST(θ),因为多了一个非终结符号 a。
      77     这是为了防止FIRST(θ)为ε的情况,这样FIRST(θa)退化为FIRST(a) = {a}
      78     closure(s) : 
      79     while (s is still changing)
      80         for each item [A -> β·Cθ,a] ∈ s        //从当前s集中寻求新状态
      81             for each production C -> γ ∈ P  //有效产生式
      82                 for each b ∈ FIRST(θa)        //如若是可能是前瞻符号(非终结符)
      83                     s <- s ∪ {[C -> ·γ,b]}
      84                     
      85     //这里x是语法符号,可以是终结符,也可以是非终结符
      86     goto(s, x)
      87     moved <- ∅
      88     for each item ∈ s        //对于s集中的每个项
      89         if the from of i is [A -> β·xθ,a] then
      90             moved <- moved ∪ {[A -> βx·θ,a]}
      91     return closure (moved)
      92     
      93     构建CC的算法:
      94     CC0 <- closure({[S' -> ·S,eof]}) //构建初始状态集
      95     CC <- {CC0}        //将初始状态集添加到规范族CC中
      96     while (new set are still being added to CC)        
      97         for each unmarked set CCi ∈ CC    //unmarked : 未标记的
      98             mark CCi as processed    //将CCi标记为处理过的
      99             for each x following a · in an item in CCi  //对于CCi中项a ·后面的每个x
     100                 temp <- goto(CCi, x)
     101                 if temp ∉ CC
     102                     then CC <- CC ∪ {temp}
     103                 record transition from CCi to temp on x
     104     例子 : 
     105     <Goal> ::= <List>;
     106     <List> ::= <List> <Pair> | <Pair>;
     107     <Pair> ::= "(" <Pair> ")" | "(" ")";
     108     closure({[Goal -> ·List,eof]})
     109     
     110     理解 : 将整个BNF范式语句全都替换成非终结符,结果可能会有很多个。
     111     但是这可以组成一个DFA,但是许多项都表示的其实是同一种状态,所以需要
     112     closure来将这些状态来并到同一个集合内,而goto则是从某个状态集接各种符号
     113     ,转移到一个新的状态集,这里即可以是终结符,也可以是非终结符。但是转移后不一定
     114     能包含所有的这一状态下的项,所以仍需要闭包运算来完善状态集。
     115     
     116     如何表示一个项?
     117     一个项包含三个元素,第一是产生式,第二是 · ,第三是前瞻符号。
     118     这可以用三个数字来表示。可以使用一个NODE来表示,
     119     但是这样好像就用不了set,来筛选是否重合了。
     120     字符串表示法?
     121     使用字符串,并且两个中隔符号隔开三个数据。
     122     如若需要,再从字符串转换为数字。
     123     
     124     3.0版本完成进度 :
     125     填表算法 : 
     126     Action表  纵轴是状态,横轴是 前瞻符号(终结符),内容是规约,状态转移,接收以及失败。
     127     Goto表  纵轴是状态,横轴是 前瞻符号(非终结符),当进行规约操作后,可以依靠栈中之前的状态,
     128     加上前瞻的非终结符,来进行状态转移。
     129     
     130     for each CCi ∈ CC
     131         for each  item I ∈ CCi
     132             if I is [A -> β·cθ,a] and goto (CCi , c) = CCj then
     133                 Action[i,c] <- "shift j"
     134             else if I is [A -> β·,a] then    //规约
     135                 Action[i,a] <- "reduce A->B"
     136             else if I is [S'->S·,eof] then    //如若是目标项产生式推导完成状态并且前瞻符号为eof,则为接收状态。
     137                 Action[i,eof] <- "accept"
     138         for each n ∈ NT                //如若项集CCi跳过一个非终结符n即到达j
     139             if goto(CCi, n) = CCj then
     140                 Goto[i,n] <- j
     141                 
     142     如何表示几种状态?
     143     可以使用位来保证两种状态不混合, shift j直接填入j,而reduce A -> B则或上整型最高位。
     144 这个图的状态 对应 书上状态
     145 0 - 0
     146 1 - 1
     147 6 - 6
     148 这里r是产生式下标...,而不是表达式下标...。
     149 因为我们采用的结构是产生式 -> 表达式,也就是一个产生式连接多个表达式。
     150 
     151 本图 对应 书上图(状态)
     152 0 - 0
     153 1 - 1
     154 6 - 6
     155 4 - 4
     156 11 - 11
     157 9 - 9
     158 2 - 3
     159 3 - 2
     160 5 - 7
     161 7 - 5
     162 10 - 8
     163 8 - 10
     164 Action表如下
     165     eof    (    )    
     166 0    err    s2    err    
     167 1    acc    s2    err    
     168 2    err    s6    s5    
     169 3    r3    r3    err    
     170 4    r2    r2    err    
     171 5    r5    r5    err    
     172 6    err    s6    s8    
     173 7    err    err    s10    
     174 8    err    err    r5    
     175 9    err    err    s11    
     176 10    r4    r4    err    
     177 11    err    err    r4    
     178 Goto表如下
     179     Goal    List    Pair    
     180 0    err    s1    s3    
     181 1    err    err    s4    
     182 2    err    err    s7    
     183 3    err    err    err    
     184 4    err    err    err    
     185 5    err    err    err    
     186 6    err    err    s9    
     187 7    err    err    err    
     188 8    err    err    err    
     189 9    err    err    err    
     190 10    err    err    err    
     191 11    err    err    err    
     192 
     193 
     194 */ 
     195 public class Demo2 {
     196     public static void main (String[] args) {
     197         //将输入的产生式都放入ch中
     198         Scanner scanner = new Scanner(System.in);
     199         String s = new String();
     200         String c;
     201         //输入处理...
     202         while (true) {
     203             c = scanner.nextLine();
     204             int i;
     205             for (i = 0; i < c.length(); i++) {
     206                 if (c.charAt(i) != '#')
     207                     s += c.charAt(i);
     208                 else {
     209                     scanner.close();
     210                     break;
     211                 }
     212             }
     213             if (i != c.length()) {
     214                 break;
     215             }
     216         }
     217         BnfContainer bc = new BnfContainer();
     218         CodeAnalyzer ca = new CodeAnalyzer(s, bc);
     219         ca.analyze();
     220         bc.toLRTable();
     221         bc.printActionAndGotoTable();
     222     }
     223 }
     224 
     225 /**
     226  * 用来装载BNF范式的信息。
     227  */
     228 class BnfContainer {
     229     /**
     230      * 内部类,NT的节点。
     231      * @author HP
     232      */
     233     class NTNode {
     234         private String name; //符号id
     235         private List<List<Integer>> expr;
     236         public NTNode(String name) {
     237             expr = new ArrayList<List<Integer>>();
     238             this.name = name;
     239         }
     240         /**
     241          * 添加一条expr
     242          * 返回这个expr的下标
     243          * @return
     244          */
     245         public int addExpr() {
     246             expr.add(new ArrayList<Integer>());
     247             return expr.size() - 1;
     248         }
     249         /**
     250          * 向下标为idx的expr添加value
     251          * @param idx
     252          * @param value
     253          */
     254         public void addExprElement (int idx, int value) {
     255             this.expr.get(idx).add(value);
     256         }
     257         /**
     258          * 向最后一个表达式添加value
     259          * @param value
     260          */
     261         public void addExprElement (int value) {
     262             this.addExprElement(this.expr.size() - 1, value);
     263         }
     264         
     265         public void printNTNode () {
     266             System.out.println("NTNumber : " + this.name);
     267             for (List<Integer> list : this.expr) {
     268                 for (Integer val : list) {
     269                     System.out.print(val + " ");
     270                 }System.out.println();
     271             }
     272         }
     273     }
     274     
     275     
     276     //常量定义
     277     /**
     278      * 这两个常量只出现在终结符
     279      * 因为要将终结符和非终结符
     280      * 放在同一个链表中
     281      * 所以使用这个来辨别终结符和非终结符。
     282      */
     283     private static final int MASK = 0X80000000; //掩码,用来给终结符做掩饰的编码。
     284     private static final int DECODE = 0X7fffffff; //解码,破译掩码得到原本的编码。
     285     private static final String separationCharacter = " ";
     286     /**
     287      * 非终结符Map 
     288      * key : 非终结符名称
     289      * value : 非终结符在production链表中的下标
     290      */
     291     private HashMap<String,Integer> NTMap;
     292     /**
     293      * 终结符Map 
     294      * key : 终结符名称
     295      * value : 终结符在T链表中的下标
     296      */
     297     private HashMap<String,Integer> TMap;
     298     // 终结符链表
     299     private ArrayList<String> T;
     300     // 产生式链表,因为一个非终结符一个产生式具有双射关系。
     301     private ArrayList<NTNode> production;
     302     //如若未设置,默认为0
     303     public int startIndex = 0;
     304     private int eof, epsilon;
     305     /**
     306      * 这个数组包含了所有非终结符的FIRST
     307      */
     308     private Set<Integer>[] First;
     309     /**
     310      * 要输出的Action表
     311      */
     312     private int[][] Action;
     313     /**
     314      * 要输出的Goto表
     315      */
     316     private int[][] Goto;
     317     
     318     public BnfContainer() {
     319         //内部数据结构初始化
     320         NTMap = new HashMap<String,Integer>();
     321         TMap = new HashMap<String,Integer>();
     322         T = new ArrayList<String>();
     323         production = new ArrayList<NTNode>();
     324         
     325         
     326         //添加两个特殊的非终结符 eof 和 ε
     327         this.addT("eof");
     328         this.addT("ε");
     329         eof = this.getTSerialNumber("eof");
     330         epsilon = this.getTSerialNumber("ε");
     331     }
     332     
     333     /**
     334      * 设置开始非终结符
     335      * @param name
     336      */
     337     public void setStart (String name) {
     338         this.addNT(name);
     339         this.startIndex = this.NTMap.get(name);
     340     }
     341     
     342     /**
     343      * 将非终结符的名字传入,即可添加一个非终结符节点。
     344      * @param name
     345      */
     346     public void addNT (String name) {
     347         if (name.isEmpty()) {
     348             System.out.println("终结符不可为空");
     349             System.exit(-1);
     350         }
     351         if (!NTMap.containsKey(name)) {
     352             NTNode node = new NTNode(name);
     353             NTMap.put(name, production.size());
     354             production.add(node);
     355         }
     356     }
     357     
     358     /**
     359      * 将终结符传入,增加非终结符。
     360      * @param name
     361      */
     362     public void addT(String name) {
     363         if (!this.TMap.containsKey(name)) {
     364             this.TMap.put(name, T.size());
     365             this.T.add(name);
     366         }
     367     }
     368     
     369     /**
     370      * 输入终结符名称
     371      * 获取终结符编号
     372      * 如若存在当前终结符,返回编号
     373      * 否则返回-1,输出错误警告并且退出。
     374      * @param name
     375      * @return
     376      */
     377     private int getTSerialNumber (String name) {
     378         this.notFindTWarning(name);
     379         return this.TMap.get(name) | BnfContainer.MASK;
     380     }
     381     
     382     /**
     383      * 输入非终结符名称
     384      * 获取非终结符编号
     385      * 如若存在当前非终结符,返回编号
     386      * 否则返回-1,输出错误警告并且退出。
     387      * @param name
     388      * @return
     389      */
     390     private int getNTSerialNumber (String name) {
     391         this.notFindNTWarning(name);
     392         return this.NTMap.get(name);
     393     }
     394     
     395     /**
     396      * 创建新的表达式并添加到名称为name的非终结符节点上
     397      * 返回表达式编号
     398      */
     399     public int creatNewExper(String name) {
     400         this.notFindNTWarning(name);
     401         NTNode ntn = this.production.get(this.NTMap.get(name));
     402         return ntn.addExpr();
     403     }
     404     /**
     405      * 向左端非终结符名称为name的产生式
     406      * 第idx表达式添加元素
     407      * @param name
     408      * @param idx
     409      * @param isNt
     410      */
     411     public void addExpeElement(String name, int idx,boolean isNt, String addElement) {
     412         NTNode ntn = this.production.get(this.NTMap.get(name));
     413         if (isNt) {
     414             this.notFindNTWarning(name);
     415             this.notFindNTWarning(addElement);
     416             ntn.addExprElement(idx, this.getNTSerialNumber(addElement));
     417         }else {
     418             this.addT(addElement);
     419             ntn.addExprElement(idx, this.getTSerialNumber(addElement));
     420         }
     421     }
     422     
     423     /**
     424      * 向左端非终结符名称为name的产生式
     425      * 最后一个表达式添加元素
     426      * @param name
     427      * @param list
     428      */
     429     public void addExpeElement(String name,boolean isNt, String addElement) {
     430         NTNode ntn = this.production.get(this.NTMap.get(name));
     431         if (isNt) {
     432             this.notFindNTWarning(name);
     433             this.notFindNTWarning(addElement);
     434             ntn.addExprElement(this.getNTSerialNumber(addElement));
     435         }else {
     436             this.addT(addElement);
     437             ntn.addExprElement(this.getTSerialNumber(addElement));
     438         }
     439     }
     440     
     441     /**
     442      * 如若找到了当前非终结符,什么都不会发生。
     443      * 否则会提示并且退出程序
     444      * @param name
     445      */
     446     private void notFindNTWarning(String name) {
     447         if (!this.NTMap.containsKey(name)) {
     448             System.out.println("错误的非终结符" + name + "!");
     449             System.exit(-1);
     450         }
     451     }
     452     /**
     453      * 如若找到了当前终结符,什么都不会发生。
     454      * 否则会提示并且退出程序
     455      * @param name
     456      */
     457     private void notFindTWarning(String name) {
     458         if (!this.TMap.containsKey(name)) {
     459             System.out.println("错误的终结符" + name + "!");
     460             System.exit(-1);
     461         }
     462     }
     463 
     464     public void printBNF() {
     465         System.out.println("开始非终结符为 : " + this.production.get(startIndex).name);
     466 //        System.out.println("终结符对应表 : ");
     467 //        for (int i = 0; i < this.T.size(); i++) {
     468 //            System.out.println(this.T.get(i) + " : " + (i | MASK));
     469 //        }
     470 //        System.out.println("非终结符对应表 : ");
     471 //        for (int i = 0; i < this.production.size(); i++) {
     472 //            System.out.println(this.production.get(i).name + " : " + i);
     473 //        }
     474         for (NTNode ntn : this.production) {
     475             ntn.printNTNode();
     476         }
     477         
     478         System.out.println("First集 : ");
     479         int count = 0;
     480         for (Set<Integer> s : First) {
     481             System.out.println("第" + count + "个非终结符" + this.production.get(count).name);
     482             for (Integer i : s) {
     483                 this.printSymbol(i);
     484             }System.out.println();
     485             count++;
     486         }
     487         System.out.println("一共有 " + this.CC.size() + " 种状态");
     488         for (Set<String> s : this.CC) {
     489             this.printCCSet(s);
     490         }
     491     }
     492     /**
     493      * 输出项集 s
     494      * @param s
     495      */
     496     private void printCCSet(Set<String> s) {
     497         for (String item : s) {
     498             this.printItem(item);
     499         }
     500         System.out.println();
     501     }
     502     
     503     
     504     private void printItem (String item) {
     505         String[] strs = item.split(BnfContainer.separationCharacter); // ! 为分隔符
     506         int productionIdx = Integer.parseInt(strs[0]); //产生式下标
     507         int exprIdx = Integer.parseInt(strs[1]); //表达式下标
     508         int placeholder = Integer.parseInt(strs[2]); //占位符下标 这个下标从0开始(表示左侧无语法符号)。
     509         int prospectiveSymbol = Integer.parseInt(strs[3]);//前瞻符
     510         NTNode ntn = this.production.get(productionIdx);
     511         System.out.print("[" + ntn.name + "::=");
     512         List<Integer> list = ntn.expr.get(exprIdx);
     513         for (int i = 0; i < list.size(); i++) {
     514             if (i == placeholder) {
     515                 System.out.print("·");
     516             }
     517             this.printSymbol(list.get(i));
     518             System.out.print(" ");
     519         }
     520         if (list.size() == placeholder) {
     521             System.out.print("·");
     522         }
     523         System.out.print(",");
     524         this.printSymbol(prospectiveSymbol);
     525         System.out.print("]	");
     526     }
     527     
     528     private void printSymbol (int sym) {
     529         if (this.isT(sym)) {
     530             System.out.print(this.T.get(sym & DECODE));
     531         }else {
     532             System.out.print(this.production.get(sym).name);
     533         }
     534     }
     535     
     536     /**
     537      * 求所有非终结符符号的FIRST集(终结符的FIRST就是它本身)
     538      * FIRST(A) : 对于语法符号A,FIRST(A)表示,
     539      * 从A推导出的符号串的第一个单词所对应的终结符的集合。
     540      */
     541     private void FIRSTAllSymbol() {
     542         First = new Set[this.production.size()];
     543         for (int i = First.length - 1; i >= 0; i--) {
     544             FIRST(i);
     545         }return;
     546     }
     547     /**
     548      * 输入非终结符下标
     549      */
     550     private void FIRST(int idx) {
     551         if (First[idx] != null) {
     552             return;
     553         }First[idx] = new HashSet<Integer>();
     554         List<List<Integer>> next = this.production.get(idx).expr;
     555         for (List<Integer> list : next) {
     556             int val = list.get(0);
     557             //非终结符
     558             if (this.isT(val)) {
     559                 First[idx].add(val);
     560             }else {
     561                 this.FIRST(val);
     562                 First[idx].addAll(First[val]);
     563             }
     564         }
     565     }
     566     
     567     private boolean isT (int val) {
     568         return (val & MASK) == MASK;
     569     }
     570     /**
     571      * 一个产生式项
     572      * 分别有四个元素
     573      * productionIdx : 产生式下标
     574      * exprIdx : 表达式下标
     575      * placeholder : 占位符
     576      * prospectiveSymbol : 前瞻符
     577      */
     578     /**
     579     闭包运算
     580     closure(s) : 
     581     while (s is still changing)
     582         for each item [A -> β·Cθ,a] ∈ s        //从当前s集中寻求新状态
     583             for each production C -> γ ∈ P  //有效产生式
     584                 for each b ∈ FIRST(θa)        //如若是可能是前瞻符号(非终结符)
     585                     s <- s ∪ {[C -> ·γ,b]}
     586      */
     587     private List<Set<String>> CC;
     588     private void closure (Set<String> s) {
     589         int lastSize = -1;
     590         while (lastSize != s.size()) {
     591             lastSize = s.size();
     592             Set<String> hashset = new HashSet<String>();
     593             for (String item : s) {
     594                 String[] strs = item.split(BnfContainer.separationCharacter); //  为分隔符
     595                 int productionIdx = Integer.parseInt(strs[0]); //产生式下标
     596                 int exprIdx = Integer.parseInt(strs[1]); //表达式下标
     597                 int placeholder = Integer.parseInt(strs[2]); //占位符下标 这个下标从0开始(表示左侧无语法符号)。
     598                 int prospectiveSymbol = Integer.parseInt(strs[3]);//前瞻符
     599                 List<Integer> temp = this.production.get(productionIdx).expr.get(exprIdx);
     600                 //for each item [A -> β·Cθ,a] ∈ s        //从当前s集中寻求新状态
     601                 //    for each production C -> γ ∈ P  //有效产生式
     602                 //temp.get(placeholder) 为 这里的 C
     603                 //条件为 C不是终结符 且 当前占位符未达到最右端    如若C是个终结符,那么就无法拓展,如若占位符已经到达最右端,也无法拓展。
     604                 if (placeholder < temp.size() && !this.isT(temp.get(placeholder))) {
     605                     int cIdx = temp.get(placeholder);
     606                     //先求FIRST(占位符后的串)
     607                     Set<Integer> set = this.FIRSTNextStr(temp, placeholder + 1, prospectiveSymbol);
     608                     List<List<Integer>> expr = this.production.get(cIdx).expr;
     609                     for (int i = 0; i < expr.size(); i++){
     610                         for (Integer val : set) {
     611                             String res = cIdx + BnfContainer.separationCharacter + i + BnfContainer.separationCharacter + 0 + BnfContainer.separationCharacter + val;
     612                             hashset.add(res);
     613                         }
     614                     }
     615                 }
     616             }s.addAll(hashset);
     617         }
     618         /**
     619          * 项集之间会有交集,
     620          * start : <Goal>;
     621          * <Goal> ::= <List>;
     622          * <List> ::= <List><Pair>
     623          *         |    <Pair>;
     624          * <Pair> ::= "(" <Pair> ")"
     625          *         |    "("")";
     626          * #
     627          * 书上这个例子的原项 CC0 和 CC1就重复了 [Pair ::= ·(Pair),(]
     628          * 当然还有其他的也重复了...
     629          */
     630         return;
     631     }
     632     /*
     633     goto(s, x)
     634     moved <- ∅
     635     for each item ∈ s        //对于s集中的每个项
     636         if the from of i is [A -> β·xθ,a] then
     637             moved <- moved ∪ {[A -> βx·θ,a]}
     638     return closure (moved)
     639     */
     640     private Set<String> go (Set<String> s, int x){
     641         Set<String> res = new HashSet<String>();
     642         for (String item : s) {
     643             String[] strs = item.split(BnfContainer.separationCharacter); // ! 为分隔符
     644             int productionIdx = Integer.parseInt(strs[0]); //产生式下标
     645             int exprIdx = Integer.parseInt(strs[1]); //表达式下标
     646             int placeholder = Integer.parseInt(strs[2]); //占位符下标 这个下标从0开始(表示左侧无语法符号)。
     647             int prospectiveSymbol = Integer.parseInt(strs[3]);//前瞻符
     648             List<Integer> temp = this.production.get(productionIdx).expr.get(exprIdx);
     649             String str = new String();
     650             if (placeholder + 1 <= temp.size() && temp.get(placeholder) == x) {
     651                 str = productionIdx + BnfContainer.separationCharacter + exprIdx + BnfContainer.separationCharacter + (placeholder + 1) + BnfContainer.separationCharacter + prospectiveSymbol;
     652                 res.add(str);
     653             }
     654         }
     655         this.closure(res);
     656         return res;
     657     }
     658     
     659     /**
     660      * 获取    从expr表达式中下标为idx的语法符号开始的串     的FIRST
     661      * @param expr
     662      * @param idx
     663      * @param prospectiveSymbol
     664      * @return
     665      */
     666     private Set<Integer> FIRSTNextStr (List<Integer> expr, int idx, int prospectiveSymbol){
     667         Set<Integer> res = new HashSet<Integer>();
     668         if (idx >= expr.size()) {
     669             res.add(prospectiveSymbol);
     670             return res;
     671         }
     672         //当前符号是终结符
     673         if (this.isT(expr.get(idx))) {
     674             res.add(expr.get(idx));
     675             return res;
     676         }
     677         res.addAll(First[expr.get(idx)]);
     678         //如若存在 epsilon 
     679         if (res.contains(this.epsilon)) {
     680             res.remove(this.epsilon);
     681             res.addAll(this.FIRSTNextStr(expr, idx + 1, prospectiveSymbol));
     682         }return res;
     683     }
     684     
     685     /*
     686     CC0 <- closure({[S' -> ·S,eof]}) //构建初始状态集
     687     CC <- {CC0}        //将初始状态集添加到规范族CC中
     688     while (new set are still being added to CC)        
     689         for each unmarked set CCi ∈ CC    //unmarked : 未标记的
     690             mark CCi as processed    //将CCi标记为处理过的
     691             for each x following a · in an item in CCi  //对于CCi中项a ·后面的每个x
     692                 temp <- goto(CCi, x)
     693                 if temp ∉ CC
     694                     then CC <- CC ∪ {temp}
     695                 record transition from CCi to temp on x
     696     */
     697     /*
     698      因为最后生成Action表中需要规约 reduce A - > BC 
     699      所以需要找到这个表达式的序号为了方便弄一个前缀数组
     700      记录在前i个产生式中有多少个表达式。
     701      */
     702     int[] preArr;
     703     
     704     private void initPreArr() {
     705         this.preArr = new int[this.production.size()];
     706         if (this.preArr.length > 0) {
     707             this.preArr[0] = this.production.get(0).expr.size();
     708             for (int i = 1; i < this.preArr.length; i++) {
     709                 this.preArr[i] = this.preArr[i - 1] + this.production.get(i).expr.size();
     710             }
     711         }
     712     }
     713     public void toLRTable() {
     714         //初始化。
     715         this.initPreArr();
     716         this.FIRSTAllSymbol();
     717         Set<String> CC0 = new HashSet<String>();
     718         List<List<Integer>> expr = this.production.get(startIndex).expr;
     719         for (int i = 0; i < expr.size(); i++) {
     720             CC0.add(this.startIndex + BnfContainer.separationCharacter + i + BnfContainer.separationCharacter + 0 + BnfContainer.separationCharacter + this.eof);
     721         }
     722         this.closure(CC0);
     723         CC = new ArrayList<Set<String>>();
     724         CC.add(CC0);
     725         int begin = 0;
     726         int lastSize = -1;
     727         List<Node> res = new ArrayList<Node>();
     728         int endState = -1;
     729         while (lastSize != CC.size()) {
     730             lastSize = CC.size();
     731             for (int i = begin; i < lastSize; i++) {
     732                 Set<String> s = this.CC.get(i);
     733                 for (String item : s) {
     734                     String[] strs = item.split(BnfContainer.separationCharacter); // ! 为分隔符
     735                     int productionIdx = Integer.parseInt(strs[0]); //产生式下标
     736                     int exprIdx = Integer.parseInt(strs[1]); //表达式下标
     737                     int placeholder = Integer.parseInt(strs[2]); //占位符下标 这个下标从0开始(表示左侧无语法符号)。
     738                     int prospectiveSymbol = Integer.parseInt(strs[3]);//前瞻符
     739                     List<Integer> list = this.production.get(productionIdx).expr.get(exprIdx);
     740                     if (placeholder < list.size()) {
     741                         //因为对于每个项集的每个项的前瞻符都会进行一次推导,所以这里包含所有的推导。我们只需要记录下来就可以生成表了。
     742                         int x = list.get(placeholder);
     743                         Set<String> temp = this.go(s, x);
     744                         int CCj = this.CCcontainsTheSet(temp);
     745                         if (CCj == -1) {
     746                             CC.add(temp);
     747                             CCj = this.CC.size() - 1;
     748                         }
     749                         res.add(new Node(i, x, CCj));
     750                     }
     751                     //可归约状态
     752                     else {
     753                         res.add(new Node(i, prospectiveSymbol, ((productionIdx - 1 >= 0 ? this.preArr[productionIdx - 1] : 0) + exprIdx + 1) | MASK));
     754                         if (productionIdx == this.startIndex) {
     755                             endState = i;
     756                         }
     757                     }
     758                 }
     759                 //更新begins
     760                 begin = lastSize;
     761             }
     762         }
     763         this.createActionAndGotoTable(res, endState);
     764     }
     765     
     766     /**
     767      * 这是构建表时临时记录数据的结构
     768      */
     769     class Node{
     770         int state;
     771         /**
     772          * 对于sym来说就是终结符和非终结符的编码
     773          * 也是利用这个来区别到底把val放Action
     774          * 表还是Goto表。
     775          */
     776         int sym;
     777         /**
     778          * 对于val来说
     779          * 如若是产生式规约,则将产生式的下标 | MASK作为val
     780          * 如若是正常的状态转移,则直接输入转移状态的下标。
     781          */
     782         int val;
     783         
     784         public Node(int state, int sym, int val){
     785             this.state = state;
     786             this.sym = sym;
     787             this.val = val;
     788         }
     789     }
     790     /**
     791      * 利用这个方法去看规范族CC中是否存在set
     792      * 并且会返回set在CC的下标如若存在的话
     793      * @param set
     794      * @return
     795      */
     796     private int CCcontainsTheSet (Set<String> set) {
     797         for (int i = 0; i < CC.size(); i++) {
     798             Set<String> s = CC.get(i);
     799             if (s.size() == set.size() && set.containsAll(s)) {
     800                 return i;
     801             }
     802         }return -1;
     803     }
     804     /*
     805     for each CCi ∈ CC
     806         for each  item I ∈ CCi
     807             if I is [A -> β·cθ,a] and goto (CCi , c) = CCj then
     808                 Action[i,c] <- "shift j"
     809             else if I is [A -> β·,a] then    //规约
     810                 Action[i,a] <- "reduce A->B"
     811             else if I is [S'->S·,eof] then    //如若是目标项产生式推导完成状态并且前瞻符号为eof,则为接收状态。
     812                 Action[i,eof] <- "accept"
     813         for each n ∈ NT                //如若项集CCi跳过一个非终结符n即到达j
     814             if goto(CCi, n) = CCj then
     815                 Goto[i,n] <- j
     816     */
     817     private void createActionAndGotoTable(List<Node> node, int endState) {
     818         //竖是状态 横是终结符
     819         this.Action = new int[this.CC.size()][this.T.size()];
     820         //赋初始值
     821         for (int i = this.CC.size() - 1; i >= 0; i--) {
     822             for (int j = this.T.size() - 1; j >=0; j--) {
     823                 this.Action[i][j] = -1;
     824             }
     825         }
     826         //竖是状态 横是非终结符
     827         this.Goto = new int[this.CC.size()][this.production.size()];//赋初始值
     828         for (int i = this.CC.size() - 1; i >= 0; i--) {
     829             for (int j = this.production.size() - 1; j >=0; j--) {
     830                 this.Goto[i][j] = -1;
     831             }
     832         }
     833         for (Node n : node) {
     834             //如若跨越的符号是终结符
     835             if (this.isT(n.sym)) {
     836                 Action[n.state][n.sym & DECODE] = n.val;
     837             }else {
     838                 Goto[n.state][n.sym] = n.val;
     839             }
     840         }
     841         //将接受状态设为最低值。
     842         this.Action[endState][this.eof & DECODE] = Integer.MIN_VALUE;
     843         return;
     844     }
     845     
     846     
     847     public void printActionAndGotoTable() {
     848         if (this.Action == null || this.Goto == null) {
     849             System.out.println("表未生成,请使用toLRTable函数生成表。");
     850             return;
     851         }
     852         //先输出一行终结符
     853         System.out.println("Action表如下");
     854         System.out.print("	");
     855         for (int i = 0; i < this.T.size(); i++) {
     856             if (i != (this.epsilon & DECODE)) {
     857                 System.out.print(this.T.get(i) + "	");
     858             }
     859         }
     860         System.out.print("
    ");
     861         for (int i = 0; i < this.Action.length; i++) {
     862             // 每行第一个输出i
     863             System.out.print(i + "	");
     864             for (int j = 0; j < this.Action[i].length; j++) {
     865                 if (j != (this.epsilon & DECODE)) {
     866                     if (this.Action[i][j] == -1) {
     867                         System.out.print("err	");
     868                     } // 规约操作
     869                     else if (this.Action[i][j] == Integer.MIN_VALUE) {
     870                         System.out.print("acc	");
     871                     } else if ((this.Action[i][j] & MASK) == MASK) {
     872                         System.out.print("r" + (this.Action[i][j] & DECODE) + "	");
     873                     } else {
     874                         System.out.print("s" + this.Action[i][j] + "	");
     875                     }
     876                 }
     877             }
     878             System.out.print("
    ");
     879         }
     880         System.out.println("Goto表如下");
     881         // 先输出一行非终结符
     882         System.out.print("	");
     883         for (int i = 0; i < this.production.size(); i++) {
     884             System.out.print(this.production.get(i).name + "	");
     885         }
     886         System.out.print("
    ");
     887         for (int i = 0; i < this.Goto.length; i++) {
     888             // 每行第一个输出i
     889             System.out.print(i + "	");
     890             for (int j = 0; j < this.Goto[i].length; j++) {
     891                 if (this.Goto[i][j] == -1) {
     892                     System.out.print("err	");
     893                     continue;
     894                 }
     895                 System.out.print("s" + this.Goto[i][j] + "	");
     896             }System.out.print("
    ");
     897         }
     898     }
     899 }
     900 
     901 /**
     902  * 代码分析器 可以将代码转换为信息等价的数据结构
     903  */
     904 class CodeAnalyzer {
     905     class Token{
     906         boolean isNt;
     907         String name;
     908         public Token (boolean isNt, String name) {
     909             this.isNt = isNt;
     910             this.name = name;
     911         }
     912     }
     913     private char[] text;
     914     private int textSize = 0; //字符串有效长度
     915     private int point = 0; //text解析进度的指针
     916     private BnfContainer bc;
     917     private Token token;
     918     String left; //左侧非终结符
     919     private int count = 0; //记录当前已经解析到哪个产生式了
     920     public CodeAnalyzer (String text, BnfContainer bc) {
     921         this.bc = bc;
     922         //初始化代码分析器
     923         this.initText(text);
     924         this.initStartSymbol();
     925         this.initCodeAnalyzer();
     926     }
     927     /**
     928      * 输入字符串文本,返回处理完毕的字符数组。
     929      * @param s
     930      * @return
     931      */
     932     private void initText(String s) {
     933         this.text = s.toCharArray();
     934         int idx = 0;
     935         //将字符串变为一个紧凑的字符数组(去除一些妨碍的字符)
     936         while (idx < text.length) {
     937             if (text[idx] == '
    ' || text[idx] == '
    ' || text[idx] == '	' || text[idx] == ' ') {
     938                 idx++;
     939             }else {
     940                 text[textSize++] = text[idx++];
     941             }
     942         }
     943     }
     944 
     945     private void initStartSymbol() {
     946         // 验证是否存在start:<
     947         point = 0;
     948         char[] needle = { 's', 't', 'a', 'r', 't', ':', '<' };
     949         if (textSize <= needle.length) {
     950             this.notFindStartNT();
     951         }
     952         point = 0;
     953         while (point < needle.length) {
     954             if (needle[point] == text[point]) {
     955                 point++;
     956             } else {
     957                 this.notFindStartNT();
     958             }
     959         }
     960         point = needle.length;
     961         while (point < textSize && text[point] != '>') {
     962             point++;
     963         }
     964         this.bc.setStart(new String(text, needle.length, point - needle.length));
     965         this.skip(Type.RT);
     966         this.skip(Type.SEMICOLON);
     967     }
     968     /**
     969      * 通过skip来跳过字符
     970      */
     971     enum Type{
     972         LT, //左尖括号
     973         RT, //右尖括号
     974         SEMICOLON, //分号
     975         QUOTE, //双引号
     976         OR, //
     977         COLON, // :
     978         EQ, //等于号
     979     }
     980     private void skip (Type t) {
     981         switch(t) {
     982         case LT:
     983             this.skip('<');
     984             break;
     985         case RT:
     986             this.skip('>');
     987             break;
     988         case OR:
     989             this.skip('|');
     990             break;
     991         case SEMICOLON:
     992             this.skip(';');
     993             break;
     994         case QUOTE:
     995             this.skip('"');
     996             break;
     997         case COLON:
     998             this.skip(':');
     999             break;
    1000         case EQ:
    1001             this.skip('=');
    1002             break;
    1003         }
    1004     }
    1005     private void skip (char c) {
    1006         if (point >= this.textSize || this.text[point] != c) {
    1007             System.out.println("第" + this.count + "个产生式,缺少符号  " + c);
    1008             System.exit(-1);
    1009         }
    1010         point++;
    1011     }
    1012     /**
    1013      * 报错 : 没有找到目标(开始)非终结符号! 并退出程序。
    1014      */
    1015     private void notFindStartNT() {
    1016         System.out.println("没有找到目标非终结符号!");
    1017         System.exit(-1);
    1018     }
    1019 
    1020     /**
    1021      * 之所以一开始就要添加非终结符,而不在解析BNF时候添加
    1022      * 是因为,非终结符存在定义的问题,如若 没有定义
    1023      * 但有使用(只在右侧出现,未在左侧定义),这个就是错误的。
    1024      */
    1025     private void initCodeAnalyzer() {
    1026         int idx = this.point;
    1027         this.point = 0;
    1028         this.count = 0;
    1029         while (true) {
    1030             while (this.point < textSize && text[this.point] != ';') {
    1031                 this.point++;
    1032             }this.point++;
    1033             this.count++;
    1034             //如若分号后面没有左括号
    1035             if (this.point >= textSize) {
    1036                 break;
    1037             }
    1038             String name = this.getNT();
    1039             bc.addNT(name);
    1040         }this.count = 0;
    1041         this.point = idx;
    1042     }
    1043 
    1044     /**
    1045      * BNF
    1046      * 从point开始解析字符串。
    1047      * <Goal> ::= {<Production>}
    1048      * <Production> ::= <左侧非终结符> "::=" <Expr>;
    1049      * <Expr> ::= <Term> { "|" <Term>}";";
    1050      * <Term> ::= {<Factor>};     //Term在这就是多个终结符或非终结符相连接
    1051      * <Factor> ::= <T> | <NT>
    1052      */
    1053     public void analyze() {
    1054         while (point < this.textSize) {
    1055             this.count++;
    1056             production();
    1057         }
    1058     }
    1059     
    1060     public void production(){
    1061         //先跳过左侧非终结符
    1062         this.left = this.getNT();
    1063         this.skipDefineSymol();
    1064         this.expr();
    1065     }
    1066     /**
    1067      * 跳过 ::=
    1068      */
    1069     public void skipDefineSymol() {
    1070         skip(Type.COLON);
    1071         skip(Type.COLON);
    1072         skip(Type.EQ);
    1073     }
    1074     /**
    1075      * 获取非终结符
    1076      * <xxx>
    1077      */
    1078     public String getNT () {
    1079         skip(Type.LT);
    1080         StringBuilder res = new StringBuilder();
    1081         while (this.point < this.textSize && text[this.point] != '>') {
    1082             res.append(text[this.point++]);
    1083         }
    1084         skip(Type.RT);
    1085         return res.toString();
    1086     }
    1087     
    1088     /**
    1089      * 当前指针指向 "T" 中第一个"
    1090      * @return
    1091      */
    1092     public String getT() {
    1093         this.skip(Type.QUOTE);
    1094         StringBuilder res = new StringBuilder();
    1095         while (this.point < this.textSize && this.text[this.point] != '"') {
    1096             res.append(text[this.point++]);
    1097         }
    1098         this.skip(Type.QUOTE);
    1099         return res.toString();
    1100     }
    1101     
    1102     /**
    1103      * 当前指针指向 ::= <T>... 中 = 后一个符号
    1104      */
    1105     public void expr(){
    1106         this.term();
    1107         while (this.point < this.textSize && text[this.point] == '|') {
    1108             this.skip(Type.OR);
    1109             term();
    1110         }this.skip(Type.SEMICOLON);
    1111     }
    1112     
    1113     /**
    1114      * 如若还有符号,当前符号指向 终结符或非终结符的符号  < 或者 "
    1115      */
    1116     public void term(){
    1117         //创建一个属于当前term的链表
    1118         bc.creatNewExper(this.left);
    1119         while (this.point < this.textSize && (text[this.point] == '"' || text[this.point] == '<')) {
    1120             factor();
    1121             bc.addExpeElement(this.left, token.isNt, token.name);
    1122         }
    1123     }
    1124     
    1125     /**
    1126      * 通过factor获取token
    1127      */
    1128     public void factor(){
    1129         //非终结符
    1130         if (text[this.point] == '"') {
    1131             String name = this.getT(); 
    1132             this.token = new Token(false, name);
    1133         }else {
    1134             String name = this.getNT();
    1135             token = new Token (true, name);
    1136         }
    1137     }
    1138 }

      

  • 相关阅读:
    数学基础详解 1——微积分
    logistic回归梯度上升优化算法
    决策树
    西瓜书学习笔记(1)——模型评估与选择
    关于map与set的一点理解;
    STL中的set容器的一点总结(转)
    HDOJ 题目分类
    Train Problem I(栈)
    猜数字(规律)
    Change the ball(找规律)
  • 原文地址:https://www.cnblogs.com/vizdl/p/11331278.html
Copyright © 2011-2022 走看看