最近又开始尝试用ANTLR (another tool for language recognition) 来生成 DSL的Parser. 其实这个 ANTLR 东东用得很广泛的。 Hibernate 拿它来 parse HQL, 而Spring 的 Expression Language 就是由它生成的parser 来完成的。
我想用这个的初衷是来parser Java 源代码。 想针对项目中涉及到的 比如由DAO 层的Hibernate 实现转换成JPA 实现来做些自动的工作。 这当中涉及到的要parse Java 源代码, 我们知道这个就是ANTLR的长项了。
废话少说, 开练。 ANTLR 的主要作用是生成 Lexer, Parser, Tree Walker 或者 Lexer 跟Parser 的combinor. 主要的运作方式是 我们需要定义一个 grammar 文件。 在这个grammar 文件中 我们要指定:
optionSpec
tokensSpec
attributeScopes
actions
rule1 : ... | ;
rule2 : | ;
这些部分的顺序是固定的,并且rule 在最后部分。
其实ANTLR 是吃它自己的狗粮的。我现在用的这个ANTLR 3.3 是由它的老版本2.7根据一个ANTLR.g grammar 文件来生成parser。由这个parser 来解析我们的grammar 文件,然后由它的另一个library StringTemplate 来生成我们的parser 或者lexer。 我们可以在它的官方网站上找到 https://github.com/antlr/antlr/blob/revision-3.4/tool/src/main/antlr3/org/antlr/grammar/v3/ANTLRv3.g
在这个 ANTLRv3.g 定义了它的start rule.
grammarDef
: DOC_COMMENT?
( 'lexer' {gtype=LEXER_GRAMMAR;} // pure lexer
| 'parser' {gtype=PARSER_GRAMMAR;} // pure parser
| 'tree' {gtype=TREE_GRAMMAR;} // a tree parser
| {gtype=COMBINED_GRAMMAR;} // merged parser/lexer
)
g='grammar' id ';' optionsSpec? tokensSpec? attrScope* action*
rule+
EOF
-> ^( {adaptor.create(gtype,$g)}
id DOC_COMMENT? optionsSpec? tokensSpec? attrScope* action* rule+
)
;
我们可以看到这里 最开始 我们可以指定点 comment, 然后是 grammar type ; grammar id. optionSpec (? 表示可选 ); tokenSpec(可选); attrScope (可选); action ( * 表示 0 到多个); rule (+ 表示 1 到多个)。跟我们上面所说的各部分次序必须固定的说法是一致的。
-> 是 rewrite AST 。 如果 我们在 options 里面指定 output=AST; ASTLabelType=CommonTree;
ANTLR 生成的parser的可读性也是很好的, 比起以前c 里面的lex, yy 好很多。 它采用的是递归下降的算法 会针对
每一个rule 都会生成 一个 function , 如果是最终符号会调用 match() 方法。 这样 一个rule 生成的方法差不多是
public final TestParser.expr_return expr() throws RecognitionException {
TestParser.expr_return retval = new TestParser.expr_return();
retval.start = input.LT(1);
Object root_0 = null;
Token INT1=null;
Token char_literal2=null;
Token INT3=null;
Object INT1_tree=null;
Object char_literal2_tree=null;
Object INT3_tree=null;
try {
// com\\bwang\\antlr\\Test.g:7:6: ( INT ( '+' INT )* )
// com\\bwang\\antlr\\Test.g:7:8: INT ( '+' INT )*
{
root_0 = (Object)adaptor.nil();
INT1=(Token)match(input,INT,FOLLOW_INT_in_expr21);
INT1_tree = (Object)adaptor.create(INT1);
adaptor.addChild(root_0, INT1_tree);
// com\\bwang\\antlr\\Test.g:7:12: ( '+' INT )*
loop1:
do {
int alt1=2;
switch ( input.LA(1) ) {
case 5:
{
alt1=1;
}
break;
}
switch (alt1) {
case 1 :
// com\\bwang\\antlr\\Test.g:7:13: '+' INT
{
char_literal2=(Token)match(input,5,FOLLOW_5_in_expr24);
char_literal2_tree = (Object)adaptor.create(char_literal2);
root_0 = (Object)adaptor.becomeRoot(char_literal2_tree, root_0);
INT3=(Token)match(input,INT,FOLLOW_INT_in_expr28);
INT3_tree = (Object)adaptor.create(INT3);
adaptor.addChild(root_0, INT3_tree);
}
break;
default :
break loop1;
}
} while (true);
}
retval.stop = input.LT(-1);
retval.tree = (Object)adaptor.rulePostProcessing(root_0);
adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
}
catch (RecognitionException re) {
reportError(re);
recover(input,re);
retval.tree = (Object)adaptor.errorNode(input, retval.start, input.LT(-1), re);
}
finally {
}
return retval;
}
这个function 返回类型 TestParser.expr_return 是这样定义的 :
public static class expr_return extends ParserRuleReturnScope {
Object tree;
public Object getTree() { return tree; }
};
当然如果我们指定ASTLabelType=CommonTree, 这个tree 就不是Object 而是 CommonTree 类型的了。
如果我们没有采用 -> 改写默认的AST 树, 它是一个 AST tree node 的list, 没有明显的层次。如果 我们通过 -> ^(rootNode, child1 child2 ....) 其中第一个节点是其他节点的root 节点。这样改写后就形成了比较强的层次的AST 树。
生成的代码是怎样用的呢 :
CharStream stream = new ANTLRStringStream(uid);
UdlLexer lexer = new UdlLexer(stream);
TokenStream tokenStream = new CommonTokenStream(lexer);
UdlParser parser = new UdlParser(tokenStream);
return parser.uid();
调用的顺序通常是 charStream -> Lexer -> TokenStream -> parser 的开始rule 对应的方法。这个时候得到的就是一个AST 树。
分享到:
相关推荐
《The Definitive ANTLR Reference》是一本经典的ANTLR教程,该书对ANTLR的关键概念和技术进行了深入浅出的讲解,是ANTLR学习者不可或缺的参考书籍之一。 #### 三、核心知识点 ##### 1. Grammar(文法规则) - **...
ANTLR中文手册 ANTLR中文手册是ANTLR使用方法的详细手册,涵盖了ANTLR的主要类、ANTLR文法文件形式、生成Java类、...ANTLR中文手册详细地介绍了ANTLR的使用方法和基本概念,为开发者提供了一个系统的ANTLR学习指南。
学习ANTLR 4的参考手册时,首先需要了解的是语言、语法、解析树、词法单元和解析器的基本概念。语言由一系列有效句子组成,每个句子包含短语,而短语又由子短语组成。语法是语言的语法规则的正式定义,它指定了子...
在编译原理的学习中,ANTLR4扮演了核心角色。编译器是将高级语言转换为机器语言的程序,这一过程分为词法分析、语法分析、语义分析和代码生成四个主要阶段。ANTLR4主要用于语法分析阶段,它通过解析源代码的语法结构...
ANTLR(ANother Tool for ...通过学习ANTLR,开发者可以快速构建出高效、灵活的解析器,实现对各种结构化文本的解析和处理,这对于开发语言工具、构建DSL(领域特定语言)或者进行代码分析等工作具有极大的价值。
从标题“Definitive ANTLR 4 Reference”和描述“ANTLR 4的权威参考”可以看出,这本书详细介绍了ANTLR 4的所有核心功能和特性,是学习和使用ANTLR 4的重要资源。 ANTLR 4的主要特点包括: 1. **高级语法定义**:...
总之,"Antlr4 C++ 计算器"是一个学习ANTLR4和语言解析技术的好起点,它展示了如何使用ANTLR4生成的解析器和词法分析器在C++环境中解析和执行数学表达式。通过深入理解和实践这个项目,你可以提升对编译原理和语言...
《antlr3最终手册》作为antlr3的学习资源,被誉为是最好的学习文档和详细说明手册,它不仅对antlr3进行了全面深入的解析,还通过实际应用案例和专家观点,为读者提供了宝贵的见解和指导。以下是对该手册核心知识点的...
总之,深入学习ANTLR4的源码,不仅可以帮助我们成为ANTLR4的专家,也能加深对解析理论、编译器构造和语言设计的理解。这是一项有价值且富有挑战性的任务,对于任何从事相关领域工作的人来说都是一个宝贵的资源。
这本书的学习笔记将帮助读者深入理解ANTLR4的工作原理和实践应用。 ANTLR4的核心功能包括: 1. **语法定义**:ANTLR4允许用户使用EBNF(扩展巴科斯范式)来定义语法规则,这是一种形式化的语法描述语言。这些规则...
通过学习和使用这些资料,可以深入掌握ANTLR的使用方法,提升在语言处理方面的专业技能。 总结来说,ANTLR 4.6是一个强大的解析器生成工具,新增的Go语言支持拓宽了其应用范围,使得Go开发者也能受益于ANTLR的高效...
在《ANTLR4权威指南》这本书中,读者将全面了解ANTLR 4的工作原理,学习如何定义和使用语法规则,以及如何利用ANTLR 4进行实际项目开发。书中涵盖了从基本概念到高级特性的详细讲解,同时提供了丰富的实例和实战指导...
这本书《The Definitive ANTLR 4 Reference》是ANTLR 4的主要作者Terence Parr的著作,被誉为学习ANTLR 4的权威指南。书中详细介绍了ANTLR 4的设计理念、工作原理以及如何使用ANTLR 4来构建解析器和词法分析器。以下...
通过学习ANTLR,开发者可以更高效地处理结构化数据,减少手动编写解析代码的工作量,提升软件的可靠性和可维护性。 在提供的两个PDF文件中,《ANTLR入门_Terence Parr.pdf》应该是原版英文书籍的扫描版或电子版,...
ANTLR(ANother Tool for Language Recognition)是一个强大的...总之,“ANTLR中文文档预览版”是中文环境下学习ANTLR的重要参考资料,它将帮助开发者更高效地掌握这个强大的工具,从而在实际项目中发挥ANTLR的潜力。
Java Antlr 4 是一个...开发者可以通过研究这些示例,学习如何定义和使用 Antlr 4 语法,从而创建自己的语言解析工具或进行语言分析任务。同时,这个资源也可以作为参考,帮助解决在使用 Antlr 4 过程中遇到的问题。
通过学习和实践这些文件,你可以更好地理解如何在Eclipse中配置ANTLR开发环境,使用ANTLR 4 IDE和Xtext来构建和测试自定义的语言解析器。 总之,ANTLR和Eclipse插件的结合为Java开发者提供了一个强大的平台,用于...
在这个“ANTLR3学习以及简单的应用--使用sql语句查询集合中的对象”的主题中,我们主要关注如何利用ANTLR3解析SQL语句,以便在非关系型数据库(NoSQL)环境下进行查询操作。由于NoSQL数据库通常不支持标准SQL,因此...
ANTLR(ANother Tool for Language Recognition)是一款强大的语法解析工具,主要用于编译原理的学习和实践,特别是词法分析和语法分析方面。它是由Terence Parr教授在1989年创建的,并随着技术的发展不断更新迭代。...