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实现的主要内容,如果未能解决你的问题,请参考以下文章