如鹏网 大学生计算机学习社区

CowNew开源团队

http://www.cownew.com 邮件请联系 about521 at 163.com

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  363 随笔 :: 2 文章 :: 808 评论 :: 0 Trackbacks

#


ANTLR树分析器
                本章翻译人 CowNew开源团队 周晓

曾经的SORCERER

在ANTLR 2.xx版本中,只要增加一些树操作符,就可以帮助你建立一种中间形式的树结构(抽象语法树) 来重写语法规则和语义动作。ANTLR同样允许你去指定AST树的文法结构,因此,可以通过操作或简单遍历树结点的方式来进行文法翻译。

以前,树分析器用一个单独的工具SORCERER来生成,但是ANTLR已经取代了它的功能。ANTLR现在可以为字符流,记号流,以及树节点来建立识别器。

什么是树分析器?

分析是决定一个记号串是否能由一个文法产生的过程。ANTLR在这方面比大多数工具考虑的都要深,它把一个二维树结构看作是一串节点。这样,在ANTLR中,对记号流分析和树分析的代码生成过程来说,真正仅有的区别就变成了对超前扫描,规则方法定义头部的检测,以及对二维树结构代码生成模板的指定上。

可以分析什么类型的树?

ANTLR树分析器可以遍历实现了AST接口的任何树。AST接口是一种基于类似儿子-兄弟结点的树通用结构,有如下重要的制导方法:

  • getFirstChild: 返回第一个子结点的引用.
  • getNextSibling: 返回下一个兄弟结点的引用.

每一个AST结点有一个子女列表,一些文本和一个"记号类型"。每个树的结点都是一棵树,因此我们说树是自相似的。AST接口的完整定义如下:


/** 最小AST结点接口用于ANTLR的AST成生
*	和树遍历
*/
public interface AST {
/** 添加一个子结点到最右边 */
public void addChild(AST c);
public boolean equals(AST t);
public boolean equalsList(AST t);
public boolean equalsListPartial(AST t);
public boolean equalsTree(AST t);
public boolean equalsTreePartial(AST t);
public ASTEnumeration findAll(AST tree);
public ASTEnumeration findAllPartial(AST subtree);
/** 得到第一个子结点; 如果没有子结点则返回null */
public AST getFirstChild();
/** 得到本结点的下一个兄弟结点 */
public AST getNextSibling();
/** 得到本结点的记号文本 */
public String getText();
/** 得到本结点的记号类型 */
public int getType();
/** 得到本结点的子结点总数; 如果是叶子结点, 返回0 */
public int getNumberOfChildren();
public void initialize(int t, String txt);
public void initialize(AST t);
public void initialize(Token t);
/** 设置第一个子结点. */
public void setFirstChild(AST c);
/** 设置下一个兄弟结点. */
public void setNextSibling(AST n);
/** 设置本结点的记号文本 */
public void setText(String text);
/** 设置本结点的记号类型 */
public void setType(int ttype);
public String toString();
public String toStringList();
public String toStringTree();
}

树的语法规则

正如PCCTS1.33的SORCERER工具和ANTLR记号语法中所看到的,树语法是一个嵌入语义动作,语义断言和句法断言的EBNF规则的集合。


规则:	可选产生式1
|	可选产生式2
...
|	可选产生式n
;

每一个可选的产生式都是由一个元素列表所组成的,列表中的元素是加入了树模式的ANTLR正规语法中的一个,有如下的形式:


#( 根结点 子结点1 子结点2 ... 子结点n )

例如:下列的树模式匹配一个以PLUS为根结点,并有两个INT子结点简单树结构:


#( PLUS INT INT )

树模式的根必须是一个记号引用,但是子结点元素不限于此,它甚至可以是子规则。例如,一种常见结构是if-then-else树结构,其中的else子句声明子树是可选的:


#( IF expr stat (stat)? )
    

值得一提的是,当指定树模式和树语法后,通常,会进行满足条件的匹配而不是精确的匹配。一旦树满足给定的模式,不管剩下多少没有分析,都会报告一次匹配。例如,#( A B ),对于像#( A #(B C) D)这样有相同结构的树,不管有多长,都会报告一次匹配。

句法断言

ANTLR树分析器在工作时仅使用一个单独的超前扫描符号,这在通常情况下不是一个问题,因为这种中间形式被明确设计成利于遍历的结构。然而,偶尔也需要区别出相似的树结构。句法断言就是被用来克服有限确定的超前扫描所带来的限制。例如:在区分一元和二元减号时,可以为每一种类型的减号都创建操作结点,这样的做法可以工作的很好。但对于一个相同的根结点,使用句法断言可以区分以下结构:


expr:   ( #(MINUS expr expr) )=> #( MINUS expr expr )
|   #( MINUS expr )
...
;

赋值的次序很重要,因为第二个可选产生式是第一个可选产生式的“子集”.

语义断言

语义断言在可选产生式的开始,仅仅同可选的断言表达式合成一体,就像合成一个正规文法。语义断言在产生式的中间,当它断言失败时,也会像正规文法一样抛出异常。

一个树遍历器的例子

考虑一下如何去建立一个计算器。一个方法是建立一个分析器,这个分析器识别输入并计算表达式的值。按照这种方法,我们将会建立一个分析器来为输入的表达式创建一棵树,把表达式以这种中间形式表示,然后树分析器遍历这个树,并计算出结果。

我们的识别器, CalcParser, 通过如下的代码来定义:


class CalcParser extends Parser;
options {
buildAST = true;   // // 默认使用 CommonAST
}
expr:   mexpr (PLUS^ mexpr)* SEMI!
;
mexpr
:   atom (STAR^ atom)*
;
atom:   INT
;

PLUSSTAR记号是操作符,因此把它们作为子树的根结点,在它们后面注释上字符'^'。SEMI记号后缀有字符'!',这指出了它不应该被加入到树中。

这个计算器的词法分析定义如下:


class CalcLexer extends Lexer;
WS	:	(' '
|	'\t'
|	'\n'
|	'\r')
{ _ttype = Token.SKIP; }
;
LPAREN:	'('
;
RPAREN:	')'
;
STAR:	'*'
;
PLUS:	'+'
;
SEMI:	';'
;
INT	:	('0'..'9')+
;
    

识别器生成的树是一棵简单的表达式树。例如,输入"3*4+5"所产生的树的形式为#( + ( * 3 4 ) 5 )。为了给这种形式的树建立树遍历器,你必须要为ANTLR递归的描述树的结构:


class CalcTreeWalker extends TreeParser;
expr	:	#(PLUS expr expr)
|	#(STAR expr expr)
|	INT
;

一旦指定结构,就可以自由的嵌入语义动作去计算出结果。一个简单的实现办法就是使expr规则返回一个整型的值,然后使每一条可选产生式计算每个子树的值。下面的树文法和动作达到了我们期望的效果:


class CalcTreeWalker extends TreeParser;
expr returns [int r]
{
int a,b;
r=0;
}
:	#(PLUS a=expr b=expr) {r = a+b;}
|	#(STAR a=expr b=expr) {r = a*b;}
|	i:INT		      {r = Integer.parseInt(i.getText());}
;
    

注意到当计算表达式值得时候,没有必要指定优先级,因为它已经隐含在树的结构中了。这也解释了为什么在以中间树形式表示的时候,要比它的输入要多很多。输入的符号确实作为结点储存在树结构中,而且这种结构隐含了结点之间的关系。

要想执行分析器和树遍历器,还需要以下的代码:


import java.io.*;
import antlr.CommonAST;
import antlr.collections.AST;
class Calc {
public static void main(String[] args) {
try {
CalcLexer lexer =
new CalcLexer(new DataInputStream(System.in));
CalcParser parser = new CalcParser(lexer);
// 分析输入的表达式
parser.expr();
CommonAST t = (CommonAST)parser.getAST();
// 以LISP符号的形式输出树
System.out.println(t.toStringList());
CalcTreeWalker walker = new CalcTreeWalker();
// 遍历由分析器建立的树
int r = walker.expr(t);
System.out.println("value is "+r);
} catch(Exception e) {
System.err.println("exception: "+e);
}
}
}
    

翻译

树分析器对检查树或者从一棵树产生输出来说是很有用的,但必须要为它们添加处理树转换的代码。就像正则分析器一样,ANTLR树分析器支持buildAST选项,这类似于SORCERER的翻译模式。程序员不去修改代码,树分析器自动把输入树拷贝到作为结果的树。每一个规则都隐含(自动定义的)一个结果树。通过getAST 方法,我们可以从树分析器中获得此树的开始符号。如果要一些可选产生式和文法元素不被自动添加到输入的树上,它们后面要注释上"!"。子树可以被部分的或者全部重写。

嵌入到规则中的语义动作可以根据测试和树结构来对结果树进行设置。参考文法动作翻译章节.

一个树翻译的例子

再来看一下上面提到的简单计算器的例子,我们可以执行树翻译来代替计算表达式的值。下面树文法的动作优化了加法的恒等运算(加0)。


class CalcTreeWalker extends TreeParser;
options{
buildAST = true;	// "翻译"模式
}
expr:!  #(PLUS left:expr right:expr)
// '!'关闭自动翻译
{
// x+0 = x
if ( #right.getType()==INT &&
Integer.parseInt(#right.getText())==0 )
{
#expr = #left;
}
// 0+x = x
else if ( #left.getType()==INT &&
Integer.parseInt(#left.getText())==0 )
{
#expr = #right;
}
// x+y
else {
#expr = #(PLUS, left, right);
}
}
|   #(STAR expr expr)  // 使用自动翻译
|   i:INT
;
    

执行分析器和树翻译器的代码如下:


import java.io.*;
import antlr.CommonAST;
import antlr.collections.AST;
class Calc {
public static void main(String[] args) {
try {
CalcLexer lexer =
new CalcLexer(new DataInputStream(System.in));
CalcParser parser = new CalcParser(lexer);
// 分析输入的表达式
parser.expr();
CommonAST t = (CommonAST)parser.getAST();
// 以LISP符号的形式输出树
System.out.println(t.toLispString());
CalcTreeWalker walker = new CalcTreeWalker();
// 遍历由分析器建立的树
walker.expr(t);
// 遍历,并得到结果
t = (CommonAST)walker.getAST();
System.out.println(t.toLispString());
} catch(Exception e) {
System.err.println("exception: "+e);
}
}
}
}

检查/调试AST

当开发树分析器的时候,经常会得到分析错误。不幸的是,你的树通常异乎寻常的大,使得很难去确定AST结构错误到底在哪里。针对这种情况(当创建Java树分析器的时候,我发现它非常有用),,我创建了一个ASTFrame类(一个JFrame),这样,你就可以用Swing树视图来查看你的AST。它没有拷贝这棵树,而是用了一个TreeModel。以应用程序方式运行antlr.debug.misc.ASTFrame去或者看看Java代码Main.java。就像不确定如何去调试一样,我不确定它们在相同的包下,总之,将会在以后的ANTLR版本中给出。这里有一个简单的使用例子:

public static void main(String args[]) {
// 创建树结点
ASTFactory factory = new ASTFactory();
CommonAST r = (CommonAST)factory.create(0, "ROOT");
r.addChild((CommonAST)factory.create(0, "C1"));
r.addChild((CommonAST)factory.create(0, "C2"));
r.addChild((CommonAST)factory.create(0, "C3"));
ASTFrame frame = new ASTFrame("AST JTree Example", r);
frame.setVisible(true);
}
Version: $Id: //depot/code/org.antlr/release/antlr-2.7.6/doc/sor.html#1 $
posted @ 2007-10-29 22:26 CowNew开源团队 阅读(4488) | 评论 (5)编辑 收藏

写存储过程的时候,发现一有意思的现象:语句大概有1k多行,在编译的时候提示:
[IBM][CLI Driver][DB2/HP64] SQL0101N  语句太长或者太复杂。  SQLSTATE=54001
然后就是粗略的优化SQL语句了,什么能不要的全不要,能折行的就折行,但是,效果还是一样。
最后,就只能用这招了:更改SQL堆的大小!
把默认的堆值放大一倍后,执行顺利通过!
-- Start of generated script for NO CONNECTION
--  Dec-20-2006 at 13:20:35
#ATTACH NDE4C3D4;
UPDATE DATABASE CONFIGURATION
  FOR FWMY USING
    stmtheap 50000
  IMMEDIATE;
DETACH;
#SYNC 10;
 
-- End of generated script for NO CONNECTION

from:http://blog.sina.com.cn/s/blog_55fc875c010007zj.html
posted @ 2007-10-28 16:56 CowNew开源团队 阅读(453) | 评论 (0)编辑 收藏

        本书系统地介绍了SWT、Draw2D、GEF、JET等与Eclipse插件开发相关的基础知识,并且以实际的开发案例来演示这些知识的实战性应用,通过对这些实际开发案例的学习,读者可以非常轻松地掌握Eclipse插件开发的技能,从而开发出满足个性化需求的插件。.
        本书以一个简单而实用的枚举生成器作为入门案例,通过该案例读者能学习到扩展点、SWT、JET等Eclipse插件开发的基本技能;接着对Eclipse插件开发中的基础知识进行了介绍,并且对属性视图的使用做了重点介绍;最后以两个具有一定复杂程度的插件(Hibernate建模工具和界面设计器)为案例介绍了SWT、Draw2D、GEF、JET等技术的综合运用。..
本书不仅适合于Eclipse插件开发初学者学习,对于有一定相关开发经验的开发人员也具有很高的参考价值。

Chinapub购买地址:http://www.china-pub.com/computers/common/info.asp?id=36806

本书随书光盘下载地址:http://www.namipan.com/d/b8879fd74ea3e2b2fd092bb3c7550352f5c6f49765b57b00
posted @ 2007-10-24 22:44 CowNew开源团队 阅读(464) | 评论 (3)编辑 收藏

今天写了一个Maxthon的“进入编辑模式”插件,可以用来破解防复制的网页。插件机制真是爽呀,看来我的《网页复制大师》要彻底退休了,呵呵。
点此下载:http://www.blogjava.net/Files/huanzhugege/editit.zip

maxhon已经收录本插件:http://addons.maxthon.com/post/2007/10/23/进入编辑模式
posted @ 2007-10-24 00:12 CowNew开源团队 阅读(453) | 评论 (0)编辑 收藏

CowNewSQL多数据库翻译器在线演示已经开发完成,欢迎体验
http://211.99.196.18:6666/cownewdemo

由于访问人数众多,如果暂时无法访问,请稍后再试。
目前已经支持SQLServer、DB2、MySQL、Oracle四种数据库管理系统。
CowNewSQL源码和二进制包可以从CowNew网站(http://www.cownew.com )下载。
**********************************************
       由于种种原因,各个数据库系统的SQL语句语法以及支持的函数都不尽相同,这造成了如下两个问题:(1)使得系统在多个不同数据库之间移植变得非常困难,特别是需要维护多个数据库版本的时候;(2)开发人员必须对各种数据库的语法差异非常了解,这加大了开发难度。

       虽然Hibernate通过HQL等技术部分的解决了跨数据库移植的问题,但是在对性能要求比较高的场合还是需要直接使用SQL语句访问数据库的,在这种情况下如何编写能被不同数据库支持的SQL语句就成了。目前解决这种差异的最常用的技术就是SQL语句翻译,使用SQL翻译器可以将SQL语句翻译为在不同的数据库中支持的特定平台的SQL语句。CowNewSQL就是这样一款产品。

       CowNewSQL简化了跨数据库产品的开发,比如取当前日期在MSSQL中是“SELECT GETDATE()”,在MYSQL中是“SELECT NOW()”,在Oracle中是“SELECT SYSDATE FROM DUAL”,使用CowNewSQL以后您只要使用“SELECT NOW()”,那么CowNewSQL就会为您自动将其翻译为对应数据库平台支持的SQL语句,而且CowNewSQL的兼容性也非常好,比如“SELECT NOW()”写成“SELECT GETDATE()”同样可以被正确的翻译;取数据库前10条记录,在MSSQL中是“Select top 10 from T_1”、在MYSQL中是“SELECT  LIMIT 0, 10 ”、在Oracle中是“SELECT  FROM DUAL WHERE ROWNUM <= 10”,使用CowNewSQL以后您只要使用“Select top 10 from T_1”,那么CowNewSQL就会为您自动将其翻译为对应数据库平台支持的SQL语句。

       CowNewSQL还通过变通的方式对目标数据库不直接支持的语法进行了支持。比如MYSQL是不支持“select * from t1 where fid in(select fid from t2 limit 0,5)”这样在子查询中的Limit语句的,CowNewSQL通过将子查询进行二次结果集包装的方式巧妙的对其进行了支持,“delete from T_SaleInvoice where FId in(select top 5 FParentId from T_SaleInvoiceDetails)”通过CowNewSQL的翻译以后就成了“DELETE FROM T_SaleInvoice WHERE FId IN (select * from(SELECT FParentId FROM T_SaleInvoiceDetails LIMIT 0, 5 ) t_temp_sub)”这样被MYSQL支持的语法了;MYSQL中没有提供计算两个日期之间月份差异的函数,CowNewSQL通过组合其他日期函数的方式模拟了这个函数,这样使用者只要使用MONTHS_BETWEEN函数即可了,无需关心内部的差异。

       CowNewSQL支持如下几种类型的SQL语句:CreateTable/DropTable/CreateIndex/DropIndex/Select/Insert/Delete/Update;支持子查询、JoinUnion等高级的SQL特性;支持日期(包括取当前日期、从日期中提取任意部分、计算日期差异、日期前后推算等)、数学(包括取绝对值、取PI值、四舍五入、对数计算、随机数等)、字符串(包括取子字符串、取字符串长度、字符串截断、大小写转换等)、基本数据处理(包括数字字符串互转、日期转字符串、非空判断等)等函数。

posted @ 2007-10-21 23:39 CowNew开源团队 阅读(1368) | 评论 (10)编辑 收藏

自从2005年底discuz 4.0宣布开源以来,国内很多程序都陆续宣布开源,个个都把开源当作一把大旗来鼓吹自己,但它们真的是开源了吗?

dedecms是国内用户最多的CMS,自己搞了个协议。
discuz最初叫CDB,基于XMB 1.5 Silver开发的,CDB代码中也写了遵循BSD许可协议,但CDB后来名字变为discuz,discuz1.X已经去除了BSD授权说明……
cmsware也很可笑,减少了加密文件的数量,就宣称自己在“走向开源”。
上个月PHPCMS宣布开源了,下载了个看看,里面竟然连个license说明文件都没?难道没有任何约束和限制?

希望这些国内的优秀程序更规范一些,开源不仅仅是让别人看到源代码,也不仅仅是供别人免费使用,这不是开源的精神,开源的真正目的是让更多的人参与进来,让软件更完美。

from :http://news.csdn.net/n/20071019/109739.html
posted @ 2007-10-20 10:27 CowNew开源团队 阅读(411) | 评论 (0)编辑 收藏

    今天用通讯前置配置工具连接BICE修改其中的参数,重启BICE以后BICE报错“SAXException:文件过早结束”,用通讯前置配置工具再次连接也连不上了,而行科技处的人用他们的通讯前置配置工具修改则没事。检查配置工具的版本也一样。
    最后琢磨出来的原因:BICE服务器(包括配置服务器)是运行的JDK1.4,我机器上的JDK是1.6.所以用通讯前置配置工具修改以后保存的XML格式文件就是JDK1.6能识别的了(呵呵,貌似和XML的语言无关的理念有冲突呀),这样到JDK1.4中就识别错误了。经过验证也是确实是这个原因。
    我用的是最懒的人的解决方法:把JRE1.4拷贝到通讯前置配置工具的目录下(命名为jre1.4),然后修改通讯前置配置工具的启动脚本run.bat为:
start ./jre1.4/bin/javaw.exe -cp
./lib/commons-httpclient-2.0-beta1.jar;./lib/commons-logging.jar;./lib/jgraph.jar;./lib/PropertyPanel.jar;./lib/Utility.jar;./lib/xalan.jar;./lib/tools.jar;./lib/workflow.jar;./lib/org.mortbay.jetty.jar
cn.com.icbc.bice.ui.ConfigMainPanel

    一切OK!




posted @ 2007-10-19 19:01 CowNew开源团队 阅读(546) | 评论 (0)编辑 收藏

 

VirtualBoxforLinuxHost-Only设定

类似VMwareHost-only的作法,需要建立虚拟网卡和GuestOS来通讯。

工具 tunctl 可从 uml-utilities套件中取得。

建立虚拟网卡:

# tunctl -t vmnet1(网卡名称可自定) -u atsiv(使用者名称)

此时用ifconfig -a 查看会多出一个虚拟网卡界面。

# chmod 666 /dev/net/tun

为虚拟网卡设定 IP (私有private ip)

# ifconfig vmnet1 192.168.0.1 netmask 255.255.255.0

VirtualBox的设定:

Attached to 选择 Host Interface

Interface Name 键入 vmnet1(就是上面所设的名称)

然后在GuestOS中设定同网段的IP,例如 192.168.0.2

HostOSGuestOSPing看看是否可连通。

若要让GuestOS也可架站或连上网络,则可在HostOS中用iptables设定DNAT, SNAT即可。

删除 虚拟网卡

# tunctl -d vmnet1

目前用CentOS 4.4 母机养了一只Win2K小机。

转贴自:http://www.xuniji.com/forum/view.asp?id=7661&p=1&page=1

杨中科注:如果按照上面方法设置还ping不通的话,请查看是否是防火墙的问题,可以将GuestOSHostOS的防火墙的ping规则放开。由于我是用VirtualBox做没有网络环境时候的数据库服务器用的,不会有网络攻击的问题,所以我干脆直接将两个系统的防火墙都关掉了。对于HostOS来说只要关闭针对于虚拟网卡的防火墙就可以了。

posted @ 2007-10-17 22:16 CowNew开源团队 阅读(774) | 评论 (0)编辑 收藏

设定lib包下所有的jar到classpath
1、windows下的方法:
首先创建文件setenv.bat,内容如下:
set CLASSPATH=.\bin\
for %%f in (".\lib\*.jar") do call cpappend.bat %%f

然后创建主脚本:
if "%OS%" == "Windows_NT" setlocal
call setenv.bat
java cn.com.agree.isbp.schedule.Startup
if "%OS%" == "Windows_NT" endlocal
2、Linux下的方法:
首先创建文件setenv.sh,内容如下:
#!/bin/sh
CP=./bin/
for i in ./lib/*.jar
do
    CP="$i:$CP"
done
export CP
然后创建主脚本:
#!/bin/sh
source setenv.sh
java -cp "$CP" cn.com.agree.isbp.schedule.Startup &

posted @ 2007-10-12 15:57 CowNew开源团队 阅读(892) | 评论 (0)编辑 收藏

今天需要为产品增加Linux下的启动脚本,编写完成运行以后发现不能正常执行,而是提示:
'etenv.sh:line 4:syntax error near unexpected token do
检查脚本没有发现任何语法问题。经过自己研究发现是不同的系统下换行符造成的。我是在Windows下的Eclipse中编写的脚本,然后通过虚拟机的共享目录方式传递到虚拟机中的Linux中的,这样换行符就是Windows格式的了,使用Dos2Unix等工具或者直接使用UE的DOS转Unix功能转换以后就正常了。
posted @ 2007-10-12 15:43 CowNew开源团队 阅读(1993) | 评论 (0)编辑 收藏

仅列出标题
共30页: First 上一页 7 8 9 10 11 12 13 14 15 下一页 Last