java编写编译器和解释器
续 第二部分 初始后端实现框架后端支持编译器和解释器。现在框架抽象类Backend有两个极简版实现,一个为编译器另一个为解释器。图2-7 展示了它们的UML类图。 图2-7 子类CodeGenerator和Executor分别是后端的编译器和解析器实现。 编译器编译器后端做代码生成。backend.compiler包中的类CodeGenerator实现框架抽象类Backend。现在它被最大简化了。清单2-20 展示了它的父类方法process的实现。方法参数引用自中间码和符号表,它产生一条消息指出生成器生成了多少条机器语言指令(目前0条,因为没有实际生成过程)以及代码生成耗用时间。它调用sendMessage()方法发送编译摘要消息。 编译器约定摘要格式为:
所有COMILER_SUMMARY消息必须遵循这种约定。 清单2-20:类CodeGenerator的初级版实现 1: /** 2: * <p>编译器的后端代码生成器</p> 3: */ 4: public class CodeGenerator extends Backend 5: { 6: public void process(ICode iCode, SymTab symTab) 7: throws Exception 8: { 9: long startTime = System.currentTimeMillis(); 10: float elapsedTime = (System.currentTimeMillis() - startTime)/1000f; 11: int instructionCount = 0; 12: 13: // 发送编译摘要消息 14: sendMessage(new Message(COMPILER_SUMMARY, 15: new Number[] {instructionCount, 16: elapsedTime})); 17: } 18: } 解释器解释器后端做程序执行。包backend.interpreter中的类Executor实现框架抽象类Backend。现在它也被最大简化了。清单2-21 展示了它的父类方法process的实现。它产生一条解释状态消息表明它执行了多少条语句,运行错误数(目前都是0)以及执行程序所耗费的时间。它调用sendMessage()发送消息。 解释器约定摘要消息格式为:
所有INTERPRETER_SUMMARY消息的监听器必须遵循此种约定。 清单2-21:Executor类process()方法的初级实现 1: /** 2: * <p>解释器后端执行器</p> 3: */ 4: public class Executor extends Backend 5: { 6: public void process(ICode iCode, SymTab symTab) 7: throws Exception 8: { 9: long startTime = System.currentTimeMillis(); 10: float elapsedTime = (System.currentTimeMillis() - startTime)/1000f; 11: int executionCount = 0; 12: int runtimeErrors = 0; 13: 14: // 发送解释摘要消息 15: sendMessage(new Message(INTERPRETER_SUMMARY, 16: new Number[] {executionCount, 17: runtimeErrors, 18: elapsedTime})); 19: } 20: } 后端工厂如前端一样,后端工厂类创建合适的后端组件。 清单2-22 工厂类BackEndFactory 1: /** 2: * <p>产生编译器或解释器后端的工厂</p> 3: */ 4: public class BackendFactory 5: { 6: /** 7: * 视参数产生编译器或解释器后端 8: * @param operation "compile"或者"execute" 9: * @return 后端组件 10: * @throws Exception if an error occurred. 11: */ 12: public static Backend createBackend(String operation) 13: throws Exception 14: { 15: if (operation.equalsIgnoreCase("compile")) { 16: return new CodeGenerator(); 17: } 18: else if (operation.equalsIgnoreCase("execute")) { 19: return new Executor(); 20: } 21: else { 22: throw new Exception("后端工厂: 不支持的操作 '" + 23: operation + "'"); 24: } 25: } 26: } 静态方法createBackend()验证标识是创建一个编译器后端("compiler")还是一个解释器后端("execute")的字符串参数。如果参数正确,方法创建且返回对应的后端组件。
目前你已搞定本章的第二和第三目标,即将初级Pascal相关组件集成进框架前端以及将编译器和解释器组件集成进后端。
程序 2:程序清单框架组件以及初级具体实现组件已就绪并集成完毕。一些简单的端对端(前端对后端)测试将验证你是否正确的设计和开发了这些组件。编译器测试将调用前端解析Pascal源程序,产生一个代码清单以及打印编译后端产生的消息。类似地,解析器测试调用前端解析Pascal源程序,产生一个代码清单以及打印解释器后端产生的消息。
清单2-23 展示了Pascal主类。这个测试背后的意思是你可以编译或执行Pascal源程序。
1: /** 2: * 3: * Pascal外壳程序,根据参数选择性的调用编译器或者解释器 4: * 5: * <p>Copyright (c) 2009 by Ronald Mak</p> 6: * <p>For instructional purposes only. No warranties.</p> 7: */ 8: public class Pascal 9: { 10: private Parser parser; // 语言无关的 parser 11: private Source source; // 语言无关的 scanner 12: private ICode iCode; // 抽象语法树 13: private SymTab symTab; // 符号表 14: private Backend backend; // 后端 15: 16: /** 17: * 编译或者解释源程序 18: * @param operation compile 或者 execute 19: * @param filePath 源文件路径 20: * @param flags 命令行参数标记 21: */ 22: public Pascal(String operation, String filePath, String flags) 23: { 24: try { 25: //显示中间码结构 26: boolean intermediate = flags.indexOf('i') > -1; 27: //显示符号引用 28: boolean xref = flags.indexOf('x') > -1; 29: 30: source = new Source(new BufferedReader(new FileReader(filePath))); 31: source.addMessageListener(new SourceMessageListener()); 32: //top-down是Parser的一种,还有一种本书没有实现的bottom-up。 33: parser = FrontendFactory.createParser("Pascal", "top-down", source); 34: parser.addMessageListener(new ParserMessageListener()); 35: 36: backend = BackendFactory.createBackend(operation); 37: backend.addMessageListener(new BackendMessageListener()); 38: 39: parser.parse(); 40: source.close(); 41: //生成中间码和符号表 42: iCode = parser.getICode(); 43: symTab = parser.getSymTab(); 44: //交由后端处理 45: backend.process(iCode, symTab); 46: } 47: catch (Exception ex) { 48: System.out.println("***** 翻译器出现错误 *****"); 49: ex.printStackTrace(); 50: } 51: } 52: 53: private static final String FLAGS = "[-ix]"; 54: private static final String USAGE = 55: "使用方式: Pascal execute|compile " + FLAGS + " <源文件路径>"; 56: 57: /** 58: * 入口程序,参考Pascal构造函数的参数接受过程。<br> 59: * 例如:compile -i hello.pas 60: */ 61: public static void main(String args[]) 62: { 63: try { 64: String operation = args[0]; 65: 66: // 翻译操作类型,compile或execute 67: if (!( operation.equalsIgnoreCase("compile") 68: || operation.equalsIgnoreCase("execute"))) { 69: throw new Exception(); 70: } 71: 72: int i = 0; 73: String flags = ""; 74: 75: // 参数标识 76: while ((++i < args.length) && (args[i].charAt(0) == '-')) { 77: flags += args[i].substring(1); 78: } 79: 80: // 源文件 81: if (i < args.length) { 82: String path = args[i]; 83: new Pascal(operation, path, flags); 84: } 85: else { 86: throw new Exception(); 87: } 88: } 89: catch (Exception ex) { 90: System.out.println(USAGE); 91: } 92: } 93: 94: private static final String SOURCE_LINE_FORMAT = "%03d %s"; 95: 96: /** 97: * 源(也就是源文件)的监听器,用于监听源文件的读取情况,如果注册了监听器,源每产生一条消息比如<br> 98: * 读取了一行等,将会调用相应的监听器处理。典型的Observe模式 99: */ 100: private class SourceMessageListener implements MessageListener 101: { 102: public void messageReceived(Message message) 103: { 104: MessageType type = message.getType(); 105: Object body[] = (Object []) message.getBody(); 106: 107: switch (type) { 108: //源读取了一行 109: case SOURCE_LINE: { 110: int lineNumber = (Integer) body[0]; 111: String lineText = (String) body[1]; 112: 113: System.out.println(String.format(SOURCE_LINE_FORMAT, 114: lineNumber, lineText)); 115: break; 116: } 117: } 118: } 119: } 120: 121: private static final String PARSER_SUMMARY_FORMAT = 122: "源文件共有\t%d行。" + 123: "\n有\t%d个语法错误." + 124: "\n解析共耗费\t%.2f秒.\n"; 125: 126: /** 127: * Parser的监听器,监听来自Parser解析过程中产生的消息,还是Observe模式 128: */ 129: private class ParserMessageListener implements MessageListener 130: { 131: public void messageReceived(Message message) 132: { 133: MessageType type = message.getType(); 134: 135: switch (type) { 136: 137: case PARSER_SUMMARY: { 138: Number body[] = (Number[]) message.getBody(); 139: int statementCount = (Integer) body[0]; 140: int syntaxErrors = (Integer) body[1]; 141: float elapsedTime = (Float) body[2]; 142: System.out.println("\n----------代码解析统计信--------------"); 143: System.out.printf(PARSER_SUMMARY_FORMAT, 144: statementCount, syntaxErrors, 145: elapsedTime); 146: break; 147: } 148: } 149: } 150: } 151: 152: private static final String INTERPRETER_SUMMARY_FORMAT = 153: "共执行\t%d 条语句。" + 154: "\n运行中发生了\t%d 个错误。" + 155: "\n执行共耗费\t%.2f 秒。\n"; 156: 157: private static final String COMPILER_SUMMARY_FORMAT = 158: "共生成\t\t%d 条指令" + 159: "\n代码生成共耗费\t%.2f秒\n"; 160: 161: /** 162: * Listener for back end messages. 163: */ 164: private class BackendMessageListener implements MessageListener 165: { 166: /** 167: * Called by the back end whenever it produces a message. 168: * @param message the message. 169: */ 170: public void messageReceived(Message message) 171: { 172: MessageType type = message.getType(); 173: 174: switch (type) { 175: 176: case INTERPRETER_SUMMARY: { 177: Number body[] = (Number[]) message.getBody(); 178: int executionCount = (Integer) body[0]; 179: int runtimeErrors = (Integer) body[1]; 180: float elapsedTime = (Float) body[2]; 181: System.out.println("\n----------解释统计信息------------"); 182: System.out.printf(INTERPRETER_SUMMARY_FORMAT, 183: executionCount, runtimeErrors, 184: elapsedTime); 185: break; 186: } 187: 188: case COMPILER_SUMMARY: { 189: Number body[] = (Number[]) message.getBody(); 190: int instructionCount = (Integer) body[0]; 191: float elapsedTime = (Float) body[1]; 192: System.out.println("\n----------编译统计信--------------"); 193: System.out.printf(COMPILER_SUMMARY_FORMAT, 194: instructionCount, elapsedTime); 195: break; 196: } 197: } 198: } 199: } 200: } 如果classes目录包含相应的class文件且当前目录包含Pascal源文件hello.pas,使用类似如下的命令行去编译文件。 java –classpath classes Pascal compile hello.pas 解释源文件类似如下命令行 java –classpath classes Pascal execute hello.pas 在Eclipse更简单,因为hello.pas在根目录下,直接创建两个如下的Java Application,一个编译,一个解释,非常happy。 构造函数根据源文件路径创建一个Source对象,接着根据命令行参数调用前端和后端工厂创建相应组件。构造函数调用parser的parse()方法解析源文件,得到生成的中间码和符号表并将它们传给后端的process方法。 注意构造函数调用前端工厂为Pascal源语言创建top-down解析器但不依赖具体源语言或使用的解析器类型。源语言和解析器(parser)类型已传给命令行。同样地,Pascal类也不需要知道到底后端工厂创建的是编译器还是解释器(因为是公共的Backend抽象类)。 三个内部类分别是parser,Source和Backend的监听器。类SourceMessageListener,ParserMessageListener和BackendMessageListener分别遵循source,Parser以及Backend的消息格式约定。它们使用格式串SOURCE_LINE_FORMAT,PARSER_SUMMARY_FORMAT,INTERPRETER_SUMMARY_FORMAT以及COMPILER_SUMMARY_FORMAT,正确地输入格式化后的文本给System.out。 假设文件hello.pas包含清单2-24里的Pascal程序。 清单2-24 Pascal程序hello.pas PROGRAM hello (output);
{Write 'Hello, world.' ten times.} VAR BEGIN {hello} 则初级编译器将输出如清单2-25所示的内容。 001 PROGRAM hello (output);
初级解释器将生成如清单2-26的输出。 清单2-26: Pascal 解释器输出 001 PROGRAM hello (output); 这些端对端测试运行满足本章的第四和最后一个目标。 |
java编写编译器和解释器的更多相关文章
- 学了编译原理能否用 Java 写一个编译器或解释器?
16 个回答 默认排序 RednaxelaFX JavaScript.编译原理.编程 等 7 个话题的优秀回答者 282 人赞同了该回答 能.我一开始学编译原理的时候就是用Java写了好多小编译器和 ...
- 一个Java编写的小玩意儿--脚本语言解释器(一)
今天开始想写一个脚本语言编译器.在这个领域,我还是知道的太少了,写的这个过程肯定是艰辛的,因为之前从来没有接触过这类的东西.写在自己的博客里,算是记录自己的学习历程吧.相信将来自己有幸再回过头来看到自 ...
- [转帖]java的编译器,解释器和即时编译器概念
java的编译器,解释器和即时编译器概念 置顶 2019-04-20 13:18:55 菠萝科技 阅读数 268更多 分类专栏: java jvm虚拟机 操作系统/linux 版权声明:本文为博主 ...
- atitit.自己动手开发编译器and解释器(2) ------语法分析,语义分析,代码生成--attilax总结
atitit.自己动手开发编译器and解释器(2) ------语法分析,语义分析,代码生成--attilax总结 1. 建立AST 抽象语法树 Abstract Syntax Tree,AST) 1 ...
- Python 编译器与解释器
Python 编译器与解释器 Python的环境我们已经搭建好了,可以开始学习基础知识了.但是,在此之前,还要先说说编译器与解释器相关的内容. 如果这部分内容,让你觉得难以理解或不能完全明白,可以暂时 ...
- 11 个最佳的 Python 编译器和解释器
原作:Archie Mistry 翻译:豌豆花下猫@Python猫 原文:https://morioh.com/p/765b19f066a4 Python 是一门对初学者友好的编程语言,是一种多用途的 ...
- Python环境搭建-2 编译器和解释器
编译器与解释器 编译器/解释器:高级语言与机器之间的翻译官 都是将代码翻译成机器可以执行的二进制机器码,只不过在运行原理和翻译过程有不同而已. 那么两者有什么区别呢? 编译器:先整体编译再执行 解释器 ...
- Linux 小知识翻译 - 「编译器和解释器」
这次聊聊「编译器和解释器」. 编程语言中,有以C为代表的编译型语言和以Perl为代表的解释型语言.不管是哪种,程序都是以人类能够理解的形式记录的,这种形式计算机是无法理解的. 因此,才会有编译器和解释 ...
- java编写service详细笔记 - centos7.2实战笔记(windows类似就不在重复了)
java编写service详细笔记 - centos7.2实战笔记(windows类似就不在重复了) 目标效果(命令行启动服务): service xxxxd start #启动服务 servic ...
随机推荐
- 识别真假搜索引擎(搜索蜘蛛)方法(baidu,google,Msn,sogou,soso等)
http://www.useragentstring.com/pages/useragentstring.php 今天分析研究了两个网站的 Apache 日志,分析日志虽然很无聊,但却是很有意义的事情 ...
- 实体框架 Code First
原文:https://msdn.microsoft.com/zh-cn/en-zn/data/jj591621
- php PDO遇到的坑
<?php $dbConn = new PDO( "mysql:host=localhost;dbname=adtuu",'root','root', array( // 强 ...
- zw版【转发·台湾nvp系列Delphi例程】HALCON SigmaImage2
zw版[转发·台湾nvp系列Delphi例程]HALCON SigmaImage2 procedure TForm1.Button1Click(Sender: TObject);var op: HOp ...
- 小试---EF5.0入门实例1
现在做个小练习吧~~~ 第一步:首先新建一个数据库名字为Test;数据库里面只有一个表UserTable 脚本为: USE [master] GO /****** 对象: Database [Test ...
- JDBC报错记录
用JDBC连接oracle时 有如下问题: 问题一.java.lang.ClassNotFoundException: oracle.jdbc.driver.oracledriver 解决: 可以在环 ...
- web前端----jQuery扩展(很重要!!)
1.jQuery扩展语法 把扩展的内容就可以写到xxxx.js文件了,在主文件中直接导入就行了. 用法1.$.xxx() $.extend({ "GDP": function () ...
- where T : class含义
.NET支持的类型参数约束有以下五种: where T : struct | T必须是一个结构类型where T : class ...
- 安装使用composer基本流程
composer工作原理: 这里经过几个步骤:1.composer读取composer.json(这个文件手动建立,官网有格式),这个json是在当前执行composer目录的,如果目录下没有这个js ...
- 20145321《网络对抗技术》逆向与Bof基础
20145321<网络对抗技术>逆向与Bof基础 实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何 ...