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     输入 : 输入某文件内存地址。且内部采用   ::=   + ; 的结构输入的LR(1)语法。
  14     这里的仅支持 BNF范式内部的 终结符,非终结符,或运算,;(表示产生式结束),::=(表示为定义为)
  15     在这里不支持闭包,也就是{},因为闭包可以转换为非终结符的递归。
  16         输入文本格式 : 要求输入语法为 无回溯语法,在前瞻一个符号的情况下,总能预测正确的产生式规则。
  17             start :   //aim_name表示起始符号名称 
  18         例子 : 
  19         //这是错误的例子,不符合LR(1)语法
  20             start : ;
  21              ::= ;
  22              ::=  "+"  |  "-" ;
  23              ::=  "*"  |  "/" ;
  24              ::= "number";
  25             #
  26             
  27             
  28             start : ;
  29              ::= ;
  30              ::= ;
  31              ::= "+"  
  32                     |    "-"  
  33                     |    "ε";
  34              ::= ;
  35              ::= "*" 
  36                     |    "/" 
  37                     |    "ε";
  38              ::= "("")"
  39                     |    "num"
  40                     |    "name";
  41             #
  42             
  43             
  44             start : ;
  45              ::= ;
  46              ::= 
  47                     |    ;
  48              ::= "("  ")"
  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          ::=  { "|" }; //产生式(表达式)可以表达成多个小句子 或 起来
  63          ::=  { "+" }; // + 表示连接
  64          ::=  | 
  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      ::= ;
 106      ::=   | ;
 107      ::= "("  ")" | "(" ")";
 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> expr;
 236         public NTNode(String name) {
 237             expr = new ArrayList>();
 238             this.name = name;
 239         }
 240         /**
 241          * 添加一条expr
 242          * 返回这个expr的下标
 243          * @return
 244          */
 245         public int addExpr() {
 246             expr.add(new ArrayList());
 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 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 NTMap;
 292     /**
 293      * 终结符Map 
 294      * key : 终结符名称
 295      * value : 终结符在T链表中的下标
 296      */
 297     private HashMap TMap;
 298     // 终结符链表
 299     private ArrayList T;
 300     // 产生式链表,因为一个非终结符一个产生式具有双射关系。
 301     private ArrayList production;
 302     //如若未设置,默认为0
 303     public int startIndex = 0;
 304     private int eof, epsilon;
 305     /**
 306      * 这个数组包含了所有非终结符的FIRST
 307      */
 308     private Set[] 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();
 321         TMap = new HashMap();
 322         T = new ArrayList();
 323         production = new ArrayList();
 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 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 s : this.CC) {
 489             this.printCCSet(s);
 490         }
 491     }
 492     /**
 493      * 输出项集 s
 494      * @param s
 495      */
 496     private void printCCSet(Set 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 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();
 554         List> next = this.production.get(idx).expr;
 555         for (List 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> CC;
 588     private void closure (Set s) {
 589         int lastSize = -1;
 590         while (lastSize != s.size()) {
 591             lastSize = s.size();
 592             Set hashset = new HashSet();
 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 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 set = this.FIRSTNextStr(temp, placeholder + 1, prospectiveSymbol);
 608                     List> 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 : ;
 621          *  ::= ;
 622          *  ::= 
 623          *         |    ;
 624          *  ::= "("  ")"
 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 go (Set s, int x){
 641         Set res = new HashSet();
 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 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 FIRSTNextStr (List expr, int idx, int prospectiveSymbol){
 667         Set res = new HashSet();
 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 CC0 = new HashSet();
 718         List> 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>();
 724         CC.add(CC0);
 725         int begin = 0;
 726         int lastSize = -1;
 727         List res = new ArrayList();
 728         int endState = -1;
 729         while (lastSize != CC.size()) {
 730             lastSize = CC.size();
 731             for (int i = begin; i < lastSize; i++) {
 732                 Set 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 list = this.production.get(productionIdx).expr.get(exprIdx);
 740                     if (placeholder < list.size()) {
 741                         //因为对于每个项集的每个项的前瞻符都会进行一次推导,所以这里包含所有的推导。我们只需要记录下来就可以生成表了。
 742                         int x = list.get(placeholder);
 743                         Set 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 set) {
 797         for (int i = 0; i < CC.size(); i++) {
 798             Set 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, 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      *  ::= {}
1048      *  ::= <左侧非终结符> "::=" ;
1049      *  ::=  { "|" }";";
1050      *  ::= {};     //Term在这就是多个终结符或非终结符相连接
1051      *  ::=  | 
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      * 
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      * 当前指针指向 ::= ... 中 = 后一个符号
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(1)语法分析器生成器(生成Action表和Goto表)java实现(二))