antlr提取代码注释
1. 来由
为什么要写提取注释呢,起因是工作需要。弄这么个不太重要的功能点来讲,旨在抛砖引玉。
一般而言,大家使用antlr解析源代码的时候,不会关心注释和空格之类内容,默认会过滤掉,不会放到语法树里,讲了,真把空格这类东西保留在语法树里,会带来很多问题。要保留注释的话,也不会放进语法树里,而是会导流到不同的channel里。channel可以理解为不同的管道,源文件解析后的token会通过默认管道,而注释等其它一些元素,可以导流到自定义管道。这样既不会给解析带来额外负担,也不会丢弃任何内容。
2. 抽取注释
闲话少说,怎么提取代码里的注释呢,在 12.1 Broadcasting Tokens on Different Channels这一节专门有讲。
2.1 语法定义-导流
首先在语法文件里进行不同channel的导流定义:
先看默认的,直接扔掉了:
WS : [\t\n\r]+ -> skip
SL_COMMENT
: '//' .*? '\n' -> skip
;
- 1
- 2
- 3
- 4
- 5
重新定义-导流:
@lexer::members{
public static final int WHITESPACE = 1;
public static final int COMMENTS = 2;
}
WS : [ \t\n\r]+ -> channel(WHITESPACE); //channel(1)
SL_COMMENT
: '//' .*? '\n' -> channel(COMMENTS) //channel(2)
;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
效果如下图所示,默认的是channel 0,其它用户自定义的都是hidden channel:
2.2 按规则(位置)提取
下面是12.1节里的示例,为什么说按位置提取呢,因为它是按照某个具体的规则定义来抽取注释的。示例代码是要将变量定义右侧的注释,挪动到代码行的上面。
具体实现:
/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.List;
public class ShiftVarComments {
public static class CommentShifter extends CymbolBaseListener {
BufferedTokenStream tokens;
TokenStreamRewriter rewriter;
/** Create TokenStreamRewriter attached to token stream
* sitting between the Cymbol lexer and parser.
*/
public CommentShifter(BufferedTokenStream tokens) {
this.tokens = tokens;
rewriter = new TokenStreamRewriter(tokens);
}
@Override
public void exitVarDecl(CymbolParser.VarDeclContext ctx) {
Token semi = ctx.getStop();
int i = semi.getTokenIndex();
List<Token> cmtChannel =
tokens.getHiddenTokensToRight(i, CymbolLexer.COMMENTS);
if ( cmtChannel!=null ) {
Token cmt = cmtChannel.get(0);
if ( cmt!=null ) {
String txt = cmt.getText().substring(2);
String newCmt = "/* " + txt.trim() + " */\n";
rewriter.insertBefore(ctx.start, newCmt);
rewriter.replace(cmt, "\n");
}
}
}
}
public static void main(String[] args) throws Exception {
String inputFile = null;
if ( args.length>0 ) inputFile = args[0];
InputStream is = System.in;
if ( inputFile!=null ) {
is = new FileInputStream(inputFile);
}
ANTLRInputStream input = new ANTLRInputStream(is);
CymbolLexer lexer = new CymbolLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CymbolParser parser = new CymbolParser(tokens);
RuleContext tree = parser.file();
ParseTreeWalker walker = new ParseTreeWalker();
CommentShifter shifter = new CommentShifter(tokens);
walker.walk(shifter, tree);
System.out.print(shifter.rewriter.getText());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
从上述代码可以看到,CommentShifter继承listener模式,重载了exitVarDecl方法。在遍历parse tree的时候,会自动调用exitVarDecl,完成了注释顺序改写功能。exitVarDecl对应了语法文件里面的变量定义规则,每当有变量定义的时候,就会调用该方法。
2.3 按channel提取所有注释
上面的注释提取方法有个问题,就是只能提取相应规则的注释。函数有注释,类有注释,参数可能有注释,等等,还有很多别的地方,如果都提取的话,则要费一番周折,弄上一堆函数定义。
如果不关心注释所在的具体规则,只提取注释的话,可以遍历token,通过判断token所在的channel来实现。语法文件里将注释导流到channel(2),那么凡是channel值为2的token则为注释,这就好办了。
private static void printComments(String code){
CPP14Lexer lexer = new CPP14Lexer(new ANTLRInputStream(code));
CommonTokenStream tokens = new CommonTokenStream(lexer);
List<Token> lt = tokens.getTokens();
for(Token t:lt){
// if t is on channel 2 which is comments channel(configured in grammar file)
// simply pass t, otherwise for two adjacent comments line the first comment line will
// appear twice
if(t.getChannel() == 2) continue;
// getHiddenTokensToLeft will suffice to get all comments
// no need to call getHiddenTokensToRight
int tokenIndex = t.getTokenIndex();
List<Token> comments = tokens.getHiddenTokensToLeft(tokenIndex);
if(comments != null && comments.size() > 0){
for(Token c:comments){
System.out.println(" " + c.getText());
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
antlr提取代码注释的更多相关文章
- IT荐书|10个最“牛叉”的代码注释
下面是 网友针对“你看到过的最好的代码注释是什么样的?”这个问题给出的回答的前10条: 1. // 亲爱的维护者: // 如果你尝试了对这段程序进行‘优化’, // 并认识到这种企图是大错特错,请增加 ...
- java代码注释规范
java代码注释规范 代码注释是架起程序设计者与程序阅读者之间的通信桥梁,最大限度的提高团队开发合作效率.也是程序代码可维护性的重要环节之一.所以我们不是为写注释而写注释.下面说一下我们在诉求网二 ...
- PHPDocument 代码注释规范总结
PHPDocument 代码注释规范 1. 安装phpDocumentor(不推荐命令行安装)在http://manual.phpdoc.org/下载最新版本的PhpDoc放在web服务器目录下使得通 ...
- [转]java代码注释规范
代码注释是架起程序设计者与程序阅读者之间的通信桥梁,最大限度的提高团队开发合作效率.也是程序代码可维护性的重要环节之一.所以我们不是为写注释而写注释.下面说一下我们在诉求网二期开发中使用的代码注释规范 ...
- vs2010代码注释自动生成api文档
最近做了一些接口,提供其他人调用,要写个api文档,可是我想代码注释已经写了说明,能不能直接把代码注释生成api?于是找到以下方法 环境:vs2010 先下载安装Sandcastle 和Sandcas ...
- 【转】Objective-C代码注释和文档输出的工具和方法
http://blog.xcodev.com/blog/2013/11/01/code-comment-and-doc-gen-tools-for-objc/ 代码注释可以让代码更容易接受和使用,特别 ...
- VVDocumenter - Xcod代码注释工具
刚接触IOS开发时,发现XCODE非常的强大的,后续的代码实践中发现XOCDE的代码文档注释非常的差, 每次都要用手敲,蛋疼至极: 随着不断学习发现XCODE有代码片段内嵌一说(如:for .bloc ...
- 了解HTML的代码注释
什么是代码注释?代码注释的作用是帮助程序员标注代码的用途,过一段时间后再看你所编写的代码,就能很快想起这段代码的用途. 代码注释不仅方便程序员自己回忆起以前代码的用途,还可以帮助其他程序员很快的读懂你 ...
- C++统计代码注释行数 & 有效代码行数 & 代码注释公共行 & 函数个数
问题来源,在14年的暑假的一次小项目当中遇到了一个这样的问题,要求统计C++代码的注释行数,有效代码行数,代码注释公共行数,以及函数个数. 下面稍微解释一下问题, 1)注释行数:指有注释的行,包括有代 ...
随机推荐
- Android 广播代码的发送与接收
Android四大组件之一广播,使用的也比较多,广播可大致分为两种,一种是Android系统区域的广播,是由系统指令发出,例如:点亮屏幕广播,开机过程中的一些广播 省略-, 然而还有一种广播就是我们自 ...
- ECG心电图数据1
最近在写一篇基于小波变换的ECG信号压缩算法的论文,遇到了怎样获取ECG信号测试数据的问题,在百度和专业论坛里搜索了一番,发现也有很多朋友为此发愁.现在论文写好了,投稿中,顺便也把怎样获取和处理ECG ...
- Python学习-31.Python中集合的一些操作
add方法: s = {1,2,3} s.add(4) print(s)# {1, 2, 3, 4} 同list的append方法,若调用s.add(3),则不会有任何影响.这点与C#中的HashSe ...
- linux系统编程:setjmp和longjmp函数用法
#include <stdio.h> #include <setjmp.h> //jmp_buf:数组,保存栈信息即运行环境 jmp_buf buf; double Divid ...
- UWP开发---通过委托跨页面导航
-前言 做过.Net开发的都了解,当二级窗口操作主窗口的控件时通常用委托的方式.那么在UWP开发中,常常会遇到MainPage的二级Frame里面的内容去操作MainPage的导航跳转,具体看下图: ...
- 工信部公示网络安全示范项目 网易云易盾“自适应DDoS攻击深度检测和防御系统”入选
本文由 网易云发布. 工信部官网 2017年年底,经专家评审和遴选,中华人民共和国工业和信息化部(以下简称“工信部”)公示了2017年电信和互联网行业网络安全试点示范项目,网易云易盾的“自适应DDo ...
- 弦论(tjoi2015,bzoj3998)(sam(后缀自动机))
对于一个给定长度为\(N\)的字符串,求它的第\(K\)小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串\(S\) 第二行为两个整数\(T\)和\(K\),\(T\)为0则表示不同 ...
- Segment Tree-732. My Calendar III
Implement a MyCalendarThree class to store your events. A new event can always be added. Your class ...
- php主要用于哪几方面
1,服务端脚本,网站和web应用程序,web服务器,php解析器,web浏览器 2,命令行脚本 3,编写桌面应用程序
- C++中将二维数组(静态的和动态的)作为函数的参数传递
在C++编程中,我们经常将数组作为参数传递到另一个函数,数组的维数不同,传递方式也不同,此处将作一个总结,包括一维静态.动态数组,二维静态.动态数组. 一,一维数组(静态.动态一维数组) 1, 一维数 ...