LR语法分析器生成器(生成Action表和Goto表)java实现

Posted vizdl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LR语法分析器生成器(生成Action表和Goto表)java实现相关的知识,希望对你有一定的参考价值。

  本来这次想好好写一下博客的...结果耐心有限,又想着烂尾总比断更好些。于是还是把后续代码贴上。不过后续代码是继续贴在BNF容器里面的...可能会显得有些臃肿。但目前管不了那么多了。先贴上来吧hhh。说不定哪天觉得羞耻又改了呢。参考资料建议参考《编译器设计》一书。

  目前完成进度 : 目前已经完成了表驱动,通过函数输出这个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("]\t");
 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("\t");
 855         for (int i = 0; i < this.T.size(); i++) 
 856             if (i != (this.epsilon & DECODE)) 
 857                 System.out.print(this.T.get(i) + "\t");
 858             
 859         
 860         System.out.print("\n");
 861         for (int i = 0; i < this.Action.length; i++) 
 862             // 每行第一个输出i
 863             System.out.print(i + "\t");
 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\t");
 868                      // 规约操作
 869                     else if (this.Action[i][j] == Integer.MIN_VALUE) 
 870                         System.out.print("acc\t");
 871                      else if ((this.Action[i][j] & MASK) == MASK) 
 872                         System.out.print("r" + (this.Action[i][j] & DECODE) + "\t");
 873                      else 
 874                         System.out.print("s" + this.Action[i][j] + "\t");
 875                     
 876                 
 877             
 878             System.out.print("\n");
 879         
 880         System.out.println("Goto表如下");
 881         // 先输出一行非终结符
 882         System.out.print("\t");
 883         for (int i = 0; i < this.production.size(); i++) 
 884             System.out.print(this.production.get(i).name + "\t");
 885         
 886         System.out.print("\n");
 887         for (int i = 0; i < this.Goto.length; i++) 
 888             // 每行第一个输出i
 889             System.out.print(i + "\t");
 890             for (int j = 0; j < this.Goto[i].length; j++) 
 891                 if (this.Goto[i][j] == -1) 
 892                     System.out.print("err\t");
 893                     continue;
 894                 
 895                 System.out.print("s" + this.Goto[i][j] + "\t");
 896             System.out.print("\n");
 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] == ‘\r‘ || text[idx] == ‘\n‘ || text[idx] == ‘\t‘ || 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 

 

  

以上是关于LR语法分析器生成器(生成Action表和Goto表)java实现的主要内容,如果未能解决你的问题,请参考以下文章

读龙书学编译原理 语法分析(12)...

loadrunner生成随机数用于Action参数中

读龙书学编译原理 语法分析(15)...

编译原理LR语法分析器的设计与实现

编译原理LR语法分析器的设计与实现

编译原理LR语法分析器的设计与实现