上回说到:
[b]groovy负责词法、语法分析groovy文件,然后用asm生成普通的class文件,供jvm使用[/b]
这回稍许详细的分析一下源码。
我这里还是选用的最早期的groovy的初版。
整理好的代码可在附件下载,经过改装,可在maven2, maven3下编译通过,并导入到eclipse中。我这里就叫他为groovy0.1吧。
主程序在Compiler里,编译流程为3阶段。stageOne,stageTwo,stageThree。
[b]1.stageOneCompile[/b]
代码都在以下3个包里:
[list]
[*]org.codehaus.groovy.syntax
[*]org.codehaus.groovy.syntax.lexer 词法分析
[*]org.codehaus.groovy.syntax.parser 语法分析
[/list]
Compiler.java
大致就是Lexer负责词法分析,Parser负责语法分析,生成CST。
[b]1.1 词法分析[/b]
也就是拆词,或者有点lucene里的分词的意思。
比如 "package cheese.toast"
Lexer.nextToken每次调用返回的Token依次是
package
cheese
.
toast
[b]1.2 语法分析。生成具体语法树(CST)[/b]
可参考ParserTest.testPackageDeclaration_OneDot()
比如 "package abc.def.ghi"
调用以下代码,
解析以后变成一棵树,
package
|--- .(dot)
|--- .(dot)
| |----abc
| |----def
|----ghi
以上2步都属于Compiler.stageOneCompile
stageOneCompile会去调用Parser.compilationUnit(),得到一个解析好的语法树。
[b]2.stageTwoCompile[/b]
语义合法验证,内容暂时是空的,先略过。
[b]3. stageThreeCompile[/b]
这里除了ASTBuilder类在org.codehaus.groovy.syntax.parser以外,
还有代码集中在org.codehaus.groovy.ast包中。
Compiler.java
可以看到2个步骤,先build AST,然后dump class。
[b]3.1 生成抽象语法树(AST)[/b]
ASTBuilder.build()
关于CST和AST的区别可以参考
[url]http://eli.thegreenplace.net/2009/02/16/abstract-vs-concrete-syntax-trees[/url]
简单来说,AST就是将CST简化了,去掉以及合并了很多没用的节点,比如去掉分号啦,合并abc.def.ghi这种带有好多点的包名等等。
比如包名,CST转成AST以后,大致像下面这般模样:
package
|--- abc.def.ghi
如图,生成的抽象语法树有如下这些节点,所有的节点都是继承自ASTNode。
[img]http://dl2.iteye.com/upload/attachment/0109/7834/11d9fcad-38a7-3de4-8618-b814458de9da.png[/img]
比如最基本的,一个类的父亲节点是ClassNode,它的儿子有FieldNode和MethodNode分别代表属性和方法。
[b]3.1.1 getter/setter[/b]
我们研究下groovy在哪里自动生成getter/setter的。
查看代码ClassNode.addProperty()
可以发现在增加属性节点的同时,就给自动生成了2个方法节点,getter和setter。
[b]3.2 dumpClass[/b]
这里既是用ASM生成java字节码了,毋庸置疑,用到了有名的visitor模式。
ASM和visitor模式暂时不展开了。后面有机会再深入分析。
[b]groovy负责词法、语法分析groovy文件,然后用asm生成普通的class文件,供jvm使用[/b]
这回稍许详细的分析一下源码。
我这里还是选用的最早期的groovy的初版。
整理好的代码可在附件下载,经过改装,可在maven2, maven3下编译通过,并导入到eclipse中。我这里就叫他为groovy0.1吧。
主程序在Compiler里,编译流程为3阶段。stageOne,stageTwo,stageThree。
[b]1.stageOneCompile[/b]
代码都在以下3个包里:
[list]
[*]org.codehaus.groovy.syntax
[*]org.codehaus.groovy.syntax.lexer 词法分析
[*]org.codehaus.groovy.syntax.parser 语法分析
[/list]
Compiler.java
protected CSTNode stageOneCompile(File file)
throws Exception
{
LOG.info( "stage-1 compiling: " + file );
FileInputStream fileIn = new FileInputStream( file );
BufferedInputStream bufferedIn = new BufferedInputStream( fileIn );
InputStreamCharStream charStream = new InputStreamCharStream( bufferedIn );
try
{
Lexer lexer = new Lexer( charStream );
Parser parser = new Parser( new LexerTokenStream( lexer ) );
return parser.compilationUnit();
}
finally
{
charStream.close();
}
}
大致就是Lexer负责词法分析,Parser负责语法分析,生成CST。
[b]1.1 词法分析[/b]
也就是拆词,或者有点lucene里的分词的意思。
比如 "package cheese.toast"
Lexer.nextToken每次调用返回的Token依次是
package
cheese
.
toast
[b]1.2 语法分析。生成具体语法树(CST)[/b]
可参考ParserTest.testPackageDeclaration_OneDot()
比如 "package abc.def.ghi"
调用以下代码,
CSTNode root = parser.packageDeclaration();
解析以后变成一棵树,
package
|--- .(dot)
|--- .(dot)
| |----abc
| |----def
|----ghi
以上2步都属于Compiler.stageOneCompile
stageOneCompile会去调用Parser.compilationUnit(),得到一个解析好的语法树。
[b]2.stageTwoCompile[/b]
语义合法验证,内容暂时是空的,先略过。
[b]3. stageThreeCompile[/b]
这里除了ASTBuilder类在org.codehaus.groovy.syntax.parser以外,
还有代码集中在org.codehaus.groovy.ast包中。
Compiler.java
protected void stageThreeCompile(CSTNode compilationUnit,
File file )
throws Exception
{
ASTBuilder astBuilder = new ASTBuilder( getClassLoader() );
ClassNode[] classNodes = astBuilder.build( compilationUnit );
for ( int i = 0 ; i < classNodes.length ; ++i )
{
dumpClass( classNodes[ i ],
file );
}
}
可以看到2个步骤,先build AST,然后dump class。
[b]3.1 生成抽象语法树(AST)[/b]
ASTBuilder.build()
关于CST和AST的区别可以参考
[url]http://eli.thegreenplace.net/2009/02/16/abstract-vs-concrete-syntax-trees[/url]
简单来说,AST就是将CST简化了,去掉以及合并了很多没用的节点,比如去掉分号啦,合并abc.def.ghi这种带有好多点的包名等等。
比如包名,CST转成AST以后,大致像下面这般模样:
package
|--- abc.def.ghi
如图,生成的抽象语法树有如下这些节点,所有的节点都是继承自ASTNode。
[img]http://dl2.iteye.com/upload/attachment/0109/7834/11d9fcad-38a7-3de4-8618-b814458de9da.png[/img]
比如最基本的,一个类的父亲节点是ClassNode,它的儿子有FieldNode和MethodNode分别代表属性和方法。
[b]3.1.1 getter/setter[/b]
我们研究下groovy在哪里自动生成getter/setter的。
查看代码ClassNode.addProperty()
public void addProperty(PropertyNode node) {
FieldNode field =
new FieldNode(node.getName(), ACC_PRIVATE, node.getType(), getName(), node.getInitialValueExpression());
addField(field);
String name = node.getName();
String getterName = "get" + capitalize(name);
String setterName = "set" + capitalize(name);
Statement getterBlock = node.getGetterBlock();
if (getterBlock == null) {
getterBlock = createGetterBlock(node, field);
}
Statement setterBlock = node.getGetterBlock();
if (setterBlock == null) {
setterBlock = createSetterBlock(node, field);
}
MethodNode getter =
new MethodNode(
getterName,
node.getModifiers(),
node.getType(),
Parameter.EMPTY_ARRAY,
getterBlock);
addMethod(getter);
Parameter[] setterParameterTypes = { new Parameter(node.getType(), "value")};
MethodNode setter =
new MethodNode(setterName, node.getModifiers(), "void", setterParameterTypes, setterBlock);
addMethod(setter);
properties.add(node);
}
可以发现在增加属性节点的同时,就给自动生成了2个方法节点,getter和setter。
[b]3.2 dumpClass[/b]
这里既是用ASM生成java字节码了,毋庸置疑,用到了有名的visitor模式。
protected void dumpClass(ClassNode classNode,
File file)
throws Exception
{
ClassWriter classWriter = new ClassWriter( true );
ClassGenerator classGenerator = new ClassGenerator( classWriter,
getClassLoader(),
file.getName() );
classGenerator.visitClass( classNode );
byte[] code = classWriter.toByteArray();
File outputFile = createOutputFile( classNode.getName() );
if ( ! outputFile.getParentFile().exists() )
{
outputFile.getParentFile().mkdirs();
}
LOG.info( "generating class to: " + outputFile );
FileOutputStream out = new FileOutputStream( outputFile );
try
{
out.write( code );
}
finally
{
out.close();
}
}
ASM和visitor模式暂时不展开了。后面有机会再深入分析。