项目的完整代码在 C2j-Compiler

前言

上一篇已经正式的完成了有限状态自动机的构建和足够判断reduce的信息,接下来的任务就是根据这个有限状态自动机来完成语法分析表和根据这个表来实现语法分析

reduce信息

在完成语法分析表之前,还差最后一个任务,那就是描述reduce信息,来指导自动机是否该进行reduce操作

reduce信息在ProductionsStateNode各自的节点里完成,只要遍历节点里的产生式,如果符号“.”位于表达式的末尾,那么该节点即可根据该表达式以及表达式对应的lookAhead set得到reduce信息

reduce信息用一个map来表示,key是可以进行reduce的符号,也就是lookahead sets中的符合,value则是进行reduce操作的产生式

public HashMap<Integer, Integer> makeReduce() {HashMap<Integer, Integer> map = new HashMap<>();reduce(map, this.productions);reduce(map, this.mergedProduction);return map;}private void reduce(HashMap<Integer, Integer> map, ArrayList<Production> productions) {for (int i = 0; i < productions.size(); i++) {if (productions.get(i).canBeReduce()) {ArrayList<Integer> lookAhead = productions.get(i).getLookAheadSet();for (int j = 0; j < lookAhead.size(); j++) {map.put(lookAhead.get(j), (productions.get(i).getProductionNum()));}}}}

语法分析表的构建

语法分析表的构建主要在StateNodeManager类里,可以先忽略loadTable和storageTableToFile的逻辑,这一部分主要是为了储存这张表,能够多次使用

主要逻辑从while开始,遍历所有节点,先从跳转信息的Map里拿出跳转关系和跳转的目的节点,然后把这个跳转关系(这个本质上对应的是一开始Token枚举的标号)和目的节点的标号拷贝到另一个map里。接着拿到reduce信息,找到之前对应在lookahead set里的符号,把它们的value改写成- (进行reduce操作的产生式编号),之所以写成负数,就是为了区分shift操作。

所以HashMap<Integer, HashMap<Integer, Integer>>这个数据结构作为解析表表示:

  1. 第一个Integer表示当前节点的编号
  2. 第二个Integer表示输入字符
  3. 第三个Integer表示,如果大于0则是做shift操作,小于0则根据推导式做reduce操作
public HashMap<Integer, HashMap<Integer, Integer>> getLrStateTable() {File table = new File("lrStateTable.sb");if (table.exists()) {return loadTable();}Iterator it;if (isTransitionTableCompressed) {it = compressedStateList.iterator();} else {it = stateList.iterator();}while (it.hasNext()) {ProductionsStateNode state = (ProductionsStateNode) it.next();HashMap<Integer, ProductionsStateNode> map = transitionMap.get(state);HashMap<Integer, Integer> jump = new HashMap<>();if (map != null) {for (Map.Entry<Integer, ProductionsStateNode> item : map.entrySet()) {jump.put(item.getKey(), item.getValue().stateNum);}}HashMap<Integer, Integer> reduceMap = state.makeReduce();if (reduceMap.size() > 0) {for (Map.Entry<Integer, Integer> item : reduceMap.entrySet()) {jump.put(item.getKey(), -(item.getValue()));}}lrStateTable.put(state.stateNum, jump);}storageTableToFile(lrStateTable);return lrStateTable;}

表驱动的语法分析

语法分析的主要过程在LRStateTableParser类里,由parse方法启动.

和第二篇讲的一样需要一个输入堆栈,节点堆栈,其它的东西现在暂时不需要用到。在初始化的时候先把开始节点压入堆栈,当前输入字符设为EXT_DEF_LIST,然后拿到语法解析表

public LRStateTableParser(Lexer lexer) {this.lexer = lexer;statusStack.push(0);valueStack.push(null);lexer.advance();lexerInput = Token.EXT_DEF_LIST.ordinal();lrStateTable = StateNodeManager.getInstance().getLrStateTable();
}

语法解析的步骤:

  • 拿到当前节点和当前字符所对应的下一个操作,也就是action > 0是shift操作,action < 0是reduce操作
  • 如果进入action > 0,也就是shift操作
    1. 把当前状态节点和输入字符分别压入堆栈
    2. 这里要区分如果当前的字符是终结符,这时候就可以直接读入下一个字符
    3. 但是这里如果是非终结符,就应该直接用当前字符跳转到下一个状态。这里是一个需要注意的一个点,这里需要把当前的这个非终结符,放入到下一个节点的对应输入堆栈中,这样它进行reduce操作时弹出退栈的符号才是正确的
  • 如果action > 0,也就是reduce操作
    1. 拿到对应的产生式
    2. 把产生式右边对应的状态节点弹出堆栈
    3. 把完成reduce的这个符号放入输入堆栈
public void parse() {while (true) {Integer action = getAction(statusStack.peek(), lexerInput);if (action == null) {ConsoleDebugColor.outlnPurple("Shift for input: " + Token.values()[lexerInput].toString());System.err.println("The input is denied");return;}if (action > 0) {statusStack.push(action);text = lexer.text;// if (lexerInput == Token.RELOP.ordinal()) {//     relOperatorText = text;// }parseStack.push(lexerInput);if (Token.isTerminal(lexerInput)) {ConsoleDebugColor.outlnPurple("Shift for input: " + Token.values()[lexerInput].toString() + "   text: " + text);// Object obj = takeActionForShift(lexerInput);lexer.advance();lexerInput = lexer.lookAhead;// valueStack.push(obj);} else {lexerInput = lexer.lookAhead;}} else {if (action == 0) {ConsoleDebugColor.outlnPurple("The input can be accepted");return;}int reduceProduction = -action;Production product = ProductionManager.getInstance().getProductionByIndex(reduceProduction);ConsoleDebugColor.outlnPurple("reduce by product: ");product.debugPrint();// takeActionForReduce(reduceProduction);int rightSize = product.getRight().size();while (rightSize > 0) {parseStack.pop();// valueStack.pop();statusStack.pop();rightSize--;}lexerInput = product.getLeft();parseStack.push(lexerInput);// valueStack.push(attributeForParentNode);}}}private Integer getAction(Integer currentState, Integer currentInput) {HashMap<Integer, Integer> jump = lrStateTable.get(currentState);return jump.get(currentInput);}

歧义性语法

到现在已经完成了语法分析的所有内容,接下来就是语义分析了,但是在这之前还有一个需要说的是,我们当前构造的有限状态自动机属于LALR(1)语法,即使LALR(1)语法已经足够强大,但是依旧有LALR(1)语法处理不了的语法,如果给出的推导式不符合,那么这个有限状态自动机依旧不能正确解析,但是之前给出的语法都是符合LALR(1)语法的

小结

这一篇主要就是

  • 利用有限状态自动机和reduce信息完成语法解析表
  • 利用语法解析表实现表驱动的语法解析

转载于:https://www.cnblogs.com/secoding/p/11371502.html

从零写一个编译器(六):语法分析之表驱动语法分析相关推荐

  1. 从零写一个编译器(完结):总结和系列索引

    前言 这个系列算作我自己的学习笔记,到现在已经有十三篇了,加上这篇一共十四篇.一步一步的从词法分析到语法分析.语义分析,再到代码生成,准备在这一篇做一个总结收尾和一个这个系列以前文章的索引. (另外, ...

  2. 从零写一个编译器(三):语法分析之几个基础数据结构

    项目的完整代码在 C2j-Compiler 写在前面 这个系列算作为我自己在学习写一个编译器的过程的一些记录,算法之类的都没有记录原理性的东西,想知道原理的在龙书里都写得非常清楚,但是我自己一开始是不 ...

  3. 从零写一个编译器(一):输入系统和词法分析

    项目的完整代码在 C2j-Compiler 前言 从半抄半改的完成一个把C语言编译到Java字节码到现在也有些时间,一直想写一个系列来回顾整理一下写一个编译器的过程,也算是学习笔记吧.就从今天开始动笔 ...

  4. 从零写一个编译器(五):语法分析之自动机的缺陷和改进

    项目的完整代码在 C2j-Compiler 前言 在上一篇,已经成功的构建了有限状态自动机,但是这个自动机还存在两个问题: 无法处理shift/reduce矛盾 状态节点太多,导致自动机过大,效率较低 ...

  5. 从零写一个编译器(八):语义分析之构造符号表

    项目的完整代码在 C2j-Compiler 前言 在之前完成了描述符号表的数据结构,现在就可以正式构造符号表了.符号表的创建自然是要根据语法分析过程中走的,所以符号表的创建就在LRStateTable ...

  6. 从零写一个编译器(四):语法分析之构造有限状态自动机

    项目的完整代码在 C2j-Compiler 通过上一篇对几个构造自动机的基础数据结构的描述,现在就可以正式来构造有限状态自动机 我们先用一个小一点的语法推导式来描述这个过程 s -> e e - ...

  7. 从零写一个编译器(二):语法分析之前置知识

    项目的完整代码在 C2j-Compiler 前言 在之前完成了词法分析之后,得到了Token流,那么接下来就是实现语法分析器来输入Token流得到抽象语法树 (Abstract Syntax Tree ...

  8. 从零写一个编译器(十):编译前传之直接解释执行

    项目的完整代码在 C2j-Compiler 前言 这一篇不看也不会影响后面代码生成部分 现在经过词法分析语法分析语义分析,终于可以进入最核心的部分了.前面那部分可以称作编译器的前端,代码生成代码优化都 ...

  9. 从零写一个编译器(九):语义分析之构造抽象语法树(AST)

    项目的完整代码在 C2j-Compiler 前言 在上一篇完成了符号表的构建,下一步就是输出抽象语法树(Abstract Syntax Tree,AST) 抽象语法树(abstract syntax ...

最新文章

  1. 肝了三天,万字长文教你玩转 tcpdump,从此抓包不用愁
  2. ci mysql 事务_MySQL事务-学习笔记
  3. POJ - 3476 A Game with Colored Balls---优先队列+链表(用数组模拟)
  4. php XML文件解释类
  5. QTreeWidget
  6. pyqt5 实现右键自定义_《快速掌握PyQt5》第十七章 事件处理
  7. switch语句可以被代替吗_大空间建筑内的消防水炮可以代替喷淋装置吗
  8. 【Java】Java复习笔记-第三部分
  9. HTML5新特征、窍门和技术(1~5)
  10. python面试题之Fibonacci数列
  11. u盘pe无人值守linux,从U盘无人值守安装linux操作系统(纯实践笔记
  12. 微信开发学习日记(八):7步看懂weiphp插件机制,核心目标是响应微信请求
  13. Bamboo基础概念
  14. #centos7 创建内网yum源 OpenStack源部署
  15. 服务器系统排行榜,5大主流服务器操作系统对比导购
  16. 微型计算机控制第三版,清华大学出版社-图书详情-《微型计算机控制技术(第3版)》...
  17. 初识STM32之选型
  18. 品质催生消费升级 ACCESS集团和VTN国际品牌会员俱乐部的跨境电商之路
  19. IE 0day,2010传说中的攻击Google等公司的代码
  20. Postman中文版下载[9.12.0]

热门文章

  1. Cinder 组件详解 - 每天5分钟玩转 OpenStack(47)
  2. swift编写命令行工具
  3. 图说苹果工作站-MAC PRO
  4. SQL脚本文件的部署研究
  5. iamond operator is not supported in -source 1.5
  6. path:path在路由中的使用
  7. 双系统win7+ubuntu18.10在win7中读取linux下面的内容
  8. 大话数据结构:散列表
  9. windows下mysql中文乱码_windows下mysql中文乱码, 配置解决方法
  10. 数学狂想曲(八)——核弹当量问题, Lanchester战争模型, 随机过程