`
Salin
  • 浏览: 21648 次
  • 性别: Icon_minigender_1
  • 来自: 南京
文章分类
社区版块
存档分类
最新评论

(更新src)构建编译器,设计自己的脚本语言

阅读更多
相关贴
http://www.iteye.com/topic/405561

前几天做动态表单的时候老总给了我一个好东东:Antlr
为什么说Antlr是好东东呢?因为他很有意思
今天琢磨了1天,搞了个简单的小东西

目标:设计自己的脚本语言

先说说为啥要设计自己的脚本语言。
举一个例子:

在数据仓库领域,我们在做ETL的时候往往会从A地方取的一堆数据,加工后塞到B地方。
我们可以用java写个程序让后编译运行,让它来做这个事。
或者,我们更希望能简单点,比方说写个脚本
$source:url="jdbc:hsqldb:mem:Test",table="DB1"
$target:url="jdbc:hsqldb:mem:Test2",table="DB2"

#copy{
   $target.First_Name=$source.First_Name  
   $target.Last_Name=$source.Last_Name 
   $target.Address=$source.Address 
}

这个脚本一看就能懂吧,从一个库把数据拷到另一个库,是不是很简单 ?:D

不过问题来了,脚本写出来了,但是我们怎么运行它呢?JavaCompiler貌似不认识这个东西。 
好吧,那我们自己来做一个编译器和运行器。

不要惊讶,这并不是一个很复杂困难的工作

第一步:构建脚本编译器

理论上来说,这一步实际上是很复杂的,牵涉到递归下降,词法分析,语法分析等等好多事情,不过好在有Antlr这个东西作为我们实现编译器的基础,让我们这些不是很精通编译原理的人也能做出编译器来。
Antlr的资料网上还是很多的。

第二步:构建脚本的运行环境

脚本的运行是需要有一个上下文环境的,比方说
$source:url="jdbc:hsqldb:mem:Test",table="DB1"

<script type="text/javascript" src="http://www.iteye.com/javascripts/tinymce/themes/advanced/langs/zh.js"></script><script type="text/javascript" src="http://www.iteye.com/javascripts/tinymce/plugins/javaeye/langs/zh.js"></script>在这里,脚本中声明了一个数据源source,当我们编译脚本的时候,也需要在上下文对象(Context)中配置一个数据源对象。
看到这里,一定感觉Context的意义很抽象,先别急。

第三部:脚本的运行方式

在Antlr的帮助下,我们可以做到对于脚本的直接解释运行的,不过这里我想用另外的一种方式
将脚本转义成Java代码,然后把Java代码拷贝到一个实现准备好的模板Java类的某个方法里,然后动态编译加载这个Java类,并运行该方法

编译前的脚本
#copy{
   $target.First_Name=$source.First_Name  
   $target.Last_Name=$source.Last_Name 
   $target.Address=$source.Address 
}

这段脚本编译后变成了
String sql1 = "SELECT First_Name,Last_Name,Address FROM DB1";
String sql2 = "INSERT INTO DB2 (First_Name,Last_Name,Address) VALUES (";
ResultSet rs = sourceStm.executeQuery(sql1);
while (rs.next()) {
	targetStm.execute(sql2 + "'" + rs.getString("First_Name") + "'"
		+ "," + "'" + rs.getString("Last_Name") + "'" + "," + "'"
		+ rs.getString("Address") + "'" + ")");
}

只是写个例子,所以这段sql比较笨,不过这不是重点。
之后,把上面这段代码拷贝到下面

public class RuntimeTemplate {

	public void execute() throws Exception {

		System.out.println("开始执行");

		/* start */

		/* end */

		System.out.println("执行完毕");
	}

}

就像这样,把转义好的代码拷贝到/* start */和/* end */之间。

之所以这样画蛇添足,是出于两点考虑:
1.性能问题。
2.由于这些代码是被拷贝到Java类中的,所以对于很多脚本本身不能满足的功能,可以直接在脚本中写Java代码,编译时候直接复制过去,这样很方便:D

动态编译也很简单,两行代码的事
JavaCompilerTool compiler = ToolProvider.getSystemJavaCompilerTool();
compiler.run(null, null, null, "etl/Runtime.java");


然后重写一下ClassLoader,加载编译后的class文件,最后运行
Object obj = clz.newInstance();
clz.getMethod("init", new Class[] { Context.class }).invoke(obj, ctx);
clz.getMethod("execute", new Class[] {}).invoke(obj);

在这里,把之前准备好的Context对象传给了脚本运行对象。

OK,这样就搞定了基本的工作流程。

########################################################################

目前已经有了一定的进展,可以解释如下的脚本

$source : url = "jdbc:hsqldb:mem:Test", table = "DB1"
$target : url = "jdbc:hsqldb:mem:Test", table = "DB2"

$target.FIRST_NAME = $source.FIRST_NAME + "ABC"
$target.LAST_NAME = $source.LAST_NAME + 123.456
$target.FULL_NAME = $source.FIRST_NAME + $source.LAST_NAME
$target.ADDRESS = $source.ADDRESS

if( "ss"+"aa"=="ssaa" or (1+1!=2 and 1+1>=2) ){
   if( $source.FIRST_NAME  == "name1" ){
      #copy
   }
}

附件是SRC
LIB比较大,自行下载Antlr3.1.2
http://www.antlr.org/download.html
测试用的DB是h2sql

需求:JDK1.6(1.6可以直接调用CompilerTool,不用调外部exe了,本人比较懒 )
注意是JDK1.6,JRE不行,而且有的JDK中的类名是JavaCompilerTool,有的是JavaCompile,根据自身情况改下。

还差一个数据库字段类型转换没做,现在所有数据库字段类型都当做String来处理




  • src.rar (15.9 KB)
  • 下载次数: 117
分享到:
评论
24 楼 damoqiongqiu 2009-06-16  
支持一把,正需要类似的东西,下来看看!
23 楼 whaosoft 2009-06-14  
用了下 Antlr  还行 呵呵~~!
22 楼 cloud21 2009-06-13  
Salin 写道
相关贴
http://www.iteye.com/topic/405561

前几天做动态表单的时候老总给了我一个好东东:Antlr
为什么说Antlr是好东东呢?因为他很有意思
今天琢磨了1天,搞了个简单的小东西

目标:设计自己的脚本语言

先说说为啥要设计自己的脚本语言。
举一个例子:

在数据仓库领域,我们在做ETL的时候往往会从A地方取的一堆数据,加工后塞到B地方。
我们可以用java写个程序让后编译运行,让它来做这个事。
或者,我们更希望能简单点,比方说写个脚本
$source:url="jdbc:hsqldb:mem:Test",table="DB1"
$target:url="jdbc:hsqldb:mem:Test2",table="DB2"

#copy{
   $target.First_Name=$source.First_Name  
   $target.Last_Name=$source.Last_Name 
   $target.Address=$source.Address 
}

这个脚本一看就能懂吧,从一个库把数据拷到另一个库,是不是很简单 ?:D

不过问题来了,脚本写出来了,但是我们怎么运行它呢?JavaCompiler貌似不认识这个东西。 
好吧,那我们自己来做一个编译器和运行器。

不要惊讶,这并不是一个很复杂困难的工作

第一步:构建脚本编译器

理论上来说,这一步实际上是很复杂的,牵涉到递归下降,词法分析,语法分析等等好多事情,不过好在有Antlr这个东西作为我们实现编译器的基础,让我们这些不是很精通编译原理的人也能做出编译器来。
Antlr的资料网上还是很多的。

第二步:构建脚本的运行环境

脚本的运行是需要有一个上下文环境的,比方说
$source:url="jdbc:hsqldb:mem:Test",table="DB1"

<script type="text/javascript" src="http://www.iteye.com/javascripts/tinymce/themes/advanced/langs/zh.js"></script><script type="text/javascript" src="http://www.iteye.com/javascripts/tinymce/plugins/javaeye/langs/zh.js"></script>在这里,脚本中声明了一个数据源source,当我们编译脚本的时候,也需要在上下文对象(Context)中配置一个数据源对象。
看到这里,一定感觉Context的意义很抽象,先别急。

第三部:脚本的运行方式

在Antlr的帮助下,我们可以做到对于脚本的直接解释运行的,不过这里我想用另外的一种方式
将脚本转义成Java代码,然后把Java代码拷贝到一个实现准备好的模板Java类的某个方法里,然后动态编译加载这个Java类,并运行该方法

编译前的脚本
#copy{
   $target.First_Name=$source.First_Name  
   $target.Last_Name=$source.Last_Name 
   $target.Address=$source.Address 
}

这段脚本编译后变成了
String sql1 = "SELECT First_Name,Last_Name,Address FROM DB1";
String sql2 = "INSERT INTO DB2 (First_Name,Last_Name,Address) VALUES (";
ResultSet rs = sourceStm.executeQuery(sql1);
while (rs.next()) {
	targetStm.execute(sql2 + "'" + rs.getString("First_Name") + "'"
		+ "," + "'" + rs.getString("Last_Name") + "'" + "," + "'"
		+ rs.getString("Address") + "'" + ")");
}

只是写个例子,所以这段sql比较笨,不过这不是重点。
之后,把上面这段代码拷贝到下面

public class RuntimeTemplate {

	public void execute() throws Exception {

		System.out.println("开始执行");

		/* start */

		/* end */

		System.out.println("执行完毕");
	}

}

就像这样,把转义好的代码拷贝到/* start */和/* end */之间。

之所以这样画蛇添足,是出于两点考虑:
1.性能问题。
2.由于这些代码是被拷贝到Java类中的,所以对于很多脚本本身不能满足的功能,可以直接在脚本中写Java代码,编译时候直接复制过去,这样很方便:D

动态编译也很简单,两行代码的事
JavaCompilerTool compiler = ToolProvider.getSystemJavaCompilerTool();
compiler.run(null, null, null, "etl/Runtime.java");


然后重写一下ClassLoader,加载编译后的class文件,最后运行
Object obj = clz.newInstance();
clz.getMethod("init", new Class[] { Context.class }).invoke(obj, ctx);
clz.getMethod("execute", new Class[] {}).invoke(obj);

在这里,把之前准备好的Context对象传给了脚本运行对象。

OK,这样就搞定了基本的工作流程。

########################################################################

目前已经有了一定的进展,可以解释如下的脚本

$source : url = "jdbc:hsqldb:mem:Test", table = "DB1"
$target : url = "jdbc:hsqldb:mem:Test", table = "DB2"

$target.FIRST_NAME = $source.FIRST_NAME + "ABC"
$target.LAST_NAME = $source.LAST_NAME + 123.456
$target.FULL_NAME = $source.FIRST_NAME + $source.LAST_NAME
$target.ADDRESS = $source.ADDRESS

if( "ss"+"aa"=="ssaa" or (1+1!=2 and 1+1>=2) ){
   if( $source.FIRST_NAME  == "name1" ){
      #copy
   }
}

附件是SRC
LIB比较大,自行下载Antlr3.1.2
http://www.antlr.org/download.html
测试用的DB是h2sql

需求:JDK1.6(1.6可以直接调用CompilerTool,不用调外部exe了,本人比较懒 )
注意是JDK1.6,JRE不行,而且有的JDK中的类名是JavaCompilerTool,有的是JavaCompile,根据自身情况改下。

还差一个数据库字段类型转换没做,现在所有数据库字段类型都当做String来处理





21 楼 mikeandmore 2009-06-13  
好东西啊。。。留着。。。呵呵
20 楼 kjj 2009-06-13  
不过用这个来构建动态表单好像不太方便,用户的表单都喜欢拖拖拉拉的!!
19 楼 Salin 2009-06-13  
首页底下更新了SRC
18 楼 Bernard 2009-06-11  
Read all the articles in Venkat's "Creating DSLs in Java" series:                                 





上次分析Json就用了antlr,生成的东西给人的感觉不是很好,可能是我有代码洁癖吧。
简单的词法还是自己分析好了,有一两次的经验就好 不会很难的。
17 楼 Salin 2009-06-11  
我果真登上你的号了。。。
16 楼 crackcell 2009-06-10  
不蛮搂主说,现在很多大学的编译原理课配套的实践课就是用antlr等工具构建一个简单的解释器,或者干脆从头开始写……
15 楼 kjj 2009-06-10  
是个学习编译原理的好武器!!
14 楼 RednaxelaFX 2009-06-10  
TaoistWar 写道
Antlr主要是做什么的?

ANTLR是一个解析器生成器,可以根据语法描述来生成词法分析器、语法分析器、抽象语法树分析器。它生成的语法分析器采用LL(*)算法。能够以Java、C#、C++、Python、ActionScript 3等许多语言为生成目标语言。
之前发过几帖关于ANTLR的,这里:http://rednaxelafx.iteye.com/blog/313143

Salin 写道
多谢这位仁兄!MPS确实也是个好东西!

我这诡异的网络……今天那又能下载到MPS了 =v=
13 楼 titan 2009-06-10  
楼主为这个任务设计新的脚本语言有点杀鸡用牛刀,一般新的脚本语言跟现有的脚本语言都需要有一些语言特性上不同或改进,否则就用现成的就可以的。基本上这个应用封装几个函数,再利用现有的脚本语言python,groovy。。肯定轻松完成了。。

不过楼主如果对设计脚本语言感兴趣,就不一样了,不过最好能加入一点新的理念在里面。。
12 楼 Salin 2009-06-10  
TaoistWar 写道
Antlr主要是做什么的?

antlr主要是帮助你做词法分析和语法分析的
11 楼 Salin 2009-06-10  
RednaxelaFX 写道
==,楼主不是要做直接的运行程序,而是要做源码到源码的代码生成(这话用中文说怎么这么别扭……source-to-source code generation),这样的话ANTLR+StringTemplate虽然是Terence Parr很推荐的方式,但要这么用的话其实还有另外一种非常方便的工具:JetBrains的MPS。比起ANTLR,这个工具应更适合楼主的需求,它对编译原理之类的知识的要求更低,而且能更方便的做源码生成,外加可以很方便的生成出你的DSL对应的编辑器

MPS是免费的工具,官网上也有些视频教程,应该能很快上手……吧。

更新:刚到官网去试下新的版本,怪哉……我下载不到那安装包。jetbrains.com能连上,但是download.jetbrains.com连不上。有墙外的人能试试么?
现在下载不到MPS的安装包,不过楼主或许会有兴趣看看官网上的视频来感受一下它会不会比ANTLR更合适。我找找看以前下的版本还在不在……


多谢这位仁兄!MPS确实也是个好东西!
10 楼 TaoistWar 2009-06-10  
Antlr主要是做什么的?
9 楼 iany 2009-06-10  
ToolProvider.getSystemJavaCompilerTool is Java SE6 stuff.
8 楼 RednaxelaFX 2009-06-10  
==,楼主不是要做直接的运行程序,而是要做源码到源码的代码生成(这话用中文说怎么这么别扭……source-to-source code generation),这样的话ANTLR+StringTemplate虽然是Terence Parr很推荐的方式,但要这么用的话其实还有另外一种非常方便的工具:JetBrains的MPS。比起ANTLR,这个工具应更适合楼主的需求,它对编译原理之类的知识的要求更低,而且能更方便的做源码生成,外加可以很方便的生成出你的DSL对应的编辑器

MPS是免费的工具,官网上也有些视频教程,应该能很快上手……吧。

更新:刚到官网去试下新的版本,怪哉……我下载不到那安装包。jetbrains.com能连上,但是download.jetbrains.com连不上。有墙外的人能试试么?
现在下载不到MPS的安装包,不过楼主或许会有兴趣看看官网上的视频来感受一下它会不会比ANTLR更合适。我找找看以前下的版本还在不在……
7 楼 kjj 2009-06-09  
哈哈,楼上更搞笑哦!!!
6 楼 lovit 2009-06-09  
晕倒,,我点收藏的,点成隐藏帖了,不好意思。
5 楼 Salin 2009-06-09  
Groovy之前我也提过啊,LS的在这里发现我了可要低调啊 哈哈哈

相关推荐

    delphi实的词法分析与编译器实现,yacc&lex

    压缩包中的文件可能包括项目的历史记录(ChangeLog)、许可信息(LICENSE)、构建脚本(Makefile)、说明文档(README)、与Debian包管理相关的文件(debian)、测试用例(test)、源代码(src)以及文档资料(doc)...

    C--语言编译器,词法分析器|语法分析器|中间代码(四元式)生成.zip

    标题 "C--语言编译器,词法分析器|语法分析器|中间代码(四元式)生成" 提供了关于构建一个C--语言编译器的信息,涉及到编译器设计与实现的关键步骤:词法分析、语法分析以及中间代码(四元式)的生成。这些是编译...

    脚本引擎内核源代码之:类C/C++脚本引擎_AngelScript_2.17.2

    《深入解析AngelScript 2.17.2:构建高效脚本引擎内核》 AngelScript是一款开源的、类C/C++的脚本引擎,它允许开发者在应用程序中嵌入自定义的脚本语言,从而实现灵活的逻辑控制和动态行为。版本2.17.2是其一个重要...

    SNL编译器Web版源码

    【SNL编译器Web版源码】是一个用于在线编译和执行SNL语言的项目,它基于Java Servlet技术和Tomcat应用服务器构建。这个项目的主要目的是提供一个方便、高效的平台,使得用户可以在Web环境下编写、运行和调试SNL代码...

    rustc-1.61.0-src.tar.gz

    Rust构建系统具有一个名为x.py的Python脚本,用于引导构建编译器。 可以通过运行./x.py --help o找到有关它的更多信息。 使每个人都可以构建可靠,高效的软件。 Rust编程语言这是Rust的主要源代码存储库。 它包含...

    利用ant脚本 自动构建svn增量.docx

    - **目录结构**:构建脚本通常包含源代码目录(src)、目标目录(dest)、输出目录(dist)等,用于存放不同阶段的文件。 - **脚本内容**:定义目标(target)和任务(task),如`checkout`用于检出代码,`increment...

    VB for Android 核心代码(编译器)

    【VB for Android 核心代码(编译器)】是一个专为Android平台设计的Visual Basic (VB)编程语言实现,其核心代码用Java语言编写,依赖于Google的Simple框架。这个项目旨在为开发者提供一种使用VB语法进行Android应用...

    Lua:使用CMake构建系统的Lua脚本语言。 Lua源代码未修改,仅添加了CMake文件-lua source code

    Lua是一种轻量级的、可嵌入式的脚本语言,以其简洁、高效的语法设计和强大的功能在游戏开发、服务器管理、脚本自动化等多个领域得到广泛应用。CMake则是一种跨平台的构建工具,能够生成各种编译器的项目文件,简化了...

    cc.bat Go安装交叉编译器

    放置`cc.bat`的位置是`C:\go\src`,这表明它被设计为与Go语言的源代码目录相结合。Go的默认安装路径通常为`C:\Go`,而`src`子目录通常用于存放用户的Go项目源代码。因此,将`cc.bat`放在这里是为了方便用户直接在Go...

    基于Java的实例开发源码-SnakeScript Java游戏脚本引擎 v1.1.zip

    在这个实例中,Java不仅作为基础开发语言,还被用来构建了一个游戏脚本引擎,使得游戏逻辑可以以脚本形式编写,提高了代码的可读性和可维护性。同时,由于是软件/插件的形式,开发者可以将SnakeScript引擎集成到自己...

    qtscript-everywhere-src-5.15.0.zip

    在Qt的生态系统中,Qt Script扮演了重要的角色,它提供了一种动态、灵活的方式来编写应用程序的逻辑,使得非程序员也能通过脚本语言来操控Qt应用。本文将深入探讨"qtscript-everywhere-src-5.15.0.zip"这个压缩包中...

    visual c++ HGE游戏引擎+Lua脚本的结合使用.zip

    2. 数据驱动:通过Lua脚本,可以实现数据配置文件,如物品属性、地图信息等,使得游戏内容的更新更加灵活。 3. 用户界面:Lua也可以用来创建简单的用户界面,比如菜单、提示信息等,让开发者能快速迭代UI设计。 五...

    otp_src_25.0.3.tar.gz

    OTP(Open Telecommunications Platform)是Erlang编程语言的核心组件之一,它提供了一整套用于构建高度可...同时,Erlang/OTP的设计理念和实践经验也可以启发其他语言和平台的开发者,推动他们改进自己的软件设计。

    Keil5配置GCC编译器编译STM32工程示例

    - 预构建命令可以用于生成启动文件(如`startup_stm32f10x.s`)和链接脚本(如`stm32f10x_flash.ld`),这些通常在Keil工程中由内部工具自动生成。 - 后构建命令用于执行`arm-none-eabi-gcc`进行编译和链接,例如...

    tcl8.4.19-src.tar.gz

    tcl8.4.19-src.tar.gz是一款经典的开源脚本语言Tcl的源代码包,它为开发者提供了深入理解Tcl语言及其内部机制的机会。Tcl(Tool Command Language)是由John Ousterhout教授在1988年开发的一种轻量级、动态类型的...

    java语言的词法分析器(手动与flex)。使用cmake、c语言构建。.zip

    Java语言的词法分析器是编译器设计过程中的关键组件,它负责将源代码文本转换成一个个有意义的符号或标记,为语法分析提供输入。本项目提供了两种方法实现Java语言的词法分析器:手动编写和使用Flex工具。Flex是一种...

    Cmake编译器

    CMake使用一种简单的脚本语言,称为CMake语法规则,用于描述项目结构和构建步骤。这些规则通常存储在名为`CMakeLists.txt`的文件中。开发者通过这些规则告诉CMake如何处理源代码、链接库、配置选项等。CMake的核心...

    build-common:cc ++项目中使用的常见构建脚本

    在给定的标题和描述中,我们可以推测这个项目可能包含了一系列用于C/C++项目的构建工具和脚本,可能采用了`Python`作为实现语言。 `CMake`是一个广泛使用的跨平台的开源构建系统,它能够生成各种编译器所需的构建...

    tcl8.5.11-src.tar.gz,tk8.5.11-src.tar.gz,tcl8.6.8-src.tar.gz,tk8.6.8-src.tar.gz

    这些文件是关于TCL(Tool Command Language)和TK(Tk Toolkit)的源代码包,用于构建和开发这两种语言的最新版本。TCL是一种脚本语言,而TK是与之配套的图形用户界面(GUI)工具包,它们常被用于创建跨平台的应用...

Global site tag (gtag.js) - Google Analytics