将ANTLR生成的.tokens文件重格式化(Ruby版)-20080626更新

介绍了一种使用 Ruby 脚本对 ANTLR 生成的 .tokens 文件进行重格式化的方法,使得 token 的数值常量和名称更容易匹配。
相关链接:
[url=http://rednaxelafx.iteye.com/blog/177089]将ANTLR生成的.tokens文件重格式化(C++版)[/url]
[url=http://rednaxelafx.iteye.com/blog/177092]将ANTLR生成的.tokens文件重格式化(C#版)[/url]

ANTLR在对.g的语法文件生成Java代码的同时,还会生成一个.tokens文件来方便别的parser(例如说接在source parser后的tree parser)使用同样的token定义。
不过这tokens文件看起来不总是那么方便。举例来说,[url=http://rednaxelafx.iteye.com/blog/176524]昨天发的帖[/url]里Dolphin.g,对应的Dolphin.tokens文件如下:
Dolphin.tokens:
[code]FUNCTION=4
NullLiteral=31
WHILE=18
FloatTypeSuffix=38
OctalLiteral=33
CharacterLiteral=28
LBRACE=11
LineComment=44
FOR=19
DO=17
Exponent=37
RelationalOperator=24
HexDigit=35
BREAK=20
Identifier=5
LPAREN=6
IF=16
AssignmentOperator=15
RPAREN=7
CONTINUE=21
Comment=43
COMMA=8
AddOperator=25
RETURN=22
TypeSpecifier=10
VAR=13
HexLiteral=32
RBRACE=12
IntegerTypeSuffix=36
SEMICOLON=14
DecimalLiteral=34
AssignmentShorthandOperator=23
COLON=9
StringLiteral=29
WS=42
UnicodeEscape=40
FloatingPointLiteral=27
MulOperator=26
EscapeSequence=39
OctalEscape=41
BooleanLiteral=30[/code]

当我知道期望的token类型的名字时,上面的tokens文件可以帮我找到对应的数值常量;但当我在Eclipse里调试时,看到的只有"type",也就是token类型对应的数值常量,要找对应的名字就比较麻烦了。上上下下看得眼花。

于是我自然希望顺序能颠倒一下,数值常量在前,名字在后,并且按照数值常量的大小按升序排序。像这样:
Dolphin.tokens.txt:
[code]4=FUNCTION
5=Identifier
6=LPAREN
7=RPAREN
8=COMMA
9=COLON
10=TypeSpecifier
11=LBRACE
12=RBRACE
13=VAR
14=SEMICOLON
15=AssignmentOperator
16=IF
17=DO
18=WHILE
19=FOR
20=BREAK
21=CONTINUE
22=RETURN
23=AssignmentShorthandOperator
24=RelationalOperator
25=AddOperator
26=MulOperator
27=FloatingPointLiteral
28=CharacterLiteral
29=StringLiteral
30=BooleanLiteral
31=NullLiteral
32=HexLiteral
33=OctalLiteral
34=DecimalLiteral
35=HexDigit
36=IntegerTypeSuffix
37=Exponent
38=FloatTypeSuffix
39=EscapeSequence
40=UnicodeEscape
41=OctalEscape
42=WS
43=Comment
44=LineComment[/code]

这种苦力事情显然不值得开动笨重的Java或者C#,哦别提C++和C了。这个时候最能让我偷懒的办法就是最好的办法 XD

于是写了这样的一段Ruby脚本:
reformat.rb:
#!/usr/bin/env ruby
# reformat.rb

## Reformat a token file produced by ANTLR to the format:
## token_number=token_name

def reformat( infile, outfile )
lines = []

File.open infile, "r" do |file|
file.each do |line|
lines << "#{$2}=#{$1}" if line.chomp =~ /^([^=]+)=([0-9]+)$/
end
end

# yeah, i know this is slow, but we're not
# pushing for speed anyway
lines = lines.sort_by do |line|
line =~ /^[0-9]+/
$&.to_i
end

File.open outfile, "w" do |file|
lines.each { |line| file.puts line }
end
end

if ARGV.size != 2
puts "Usage: #{$0} [token file] [output file]"
exit
end

infile, outfile = ARGV
reformat infile, outfile


好吧,这段代码一点也不美。要是能把读写文件放在同一个循环里,顺带能排序就好了。这段代码仅有的好处就是没花多少时间去写而且功能符合我的需要,以后还可以继续用。
Ruby才刚开始用,希望有高手能指点指点怎么改进一下这代码~~

[color=blue](20080328更新:加入了下面的版本的代码。避免在传给sort_by的block中使用正则表达式。虽然在循环里用了to_i还是稍嫌不爽)
(20080329更新:发觉应该用Enumerable#grep的。也不用在sort_by的block里用to_i了。很好,更简洁了)
(20080626更新:File.open还是用带block的方式好。IO对象不关掉始终是不放心。另外,原本定义了一个类当作tuple用,想想其实没必要定义个类,直接用数组就够了)[/color]
reformat.rb:
#!/usr/bin/env ruby
# reformat.rb

## Reformat a token file produced by ANTLR to the format:
## token_number=token_name

def reformat( infile, outfile )
lines = []
File.open infile, "r" do |file|
file.grep /^([^=]+)=([0-9]+)$/ do |line|
lines.push [ $1, $2.to_i ] # name, value = $1, $2
end
end
lines = lines.sort_by { |pair| pair[1] }
File.open outfile, "w" do |file|
lines.each { |pair| file.puts "#{pair[1]}=#{pair[0]}" }
end
end

if ARGV.size != 2
puts "Usage: #{$0} [token file] [output file]"
exit
end

infile, outfile = ARGV
reformat infile, outfile


[color=blue](20100812更新:
当初写的Ruby跟现在写的Ruby果然还是比较不一样……)[/color]
#!/usr/bin/env ruby

def reformat(infile, outfile)
lines = File.readlines(infile).map {|l| l.chomp.split '='}.sort_by {|l| l.last.to_i}
File.open(outfile, 'w') {|f| f.puts lines.map {|l| l.reverse.join('=')}}
end

$*.size != 2 || reformat(*$*) and puts "Usage: #{$0} [token file] [output file]"

[color=blue]本来还是写ARGV比较好,不过能凑出(*$*)这么可爱的表情实在是忍不住想把ARGV写成$*啊 =_=|||[/color]

====================================================================

P.S. 当然这东西不用Ruby还有更快的实现方法,直接用UltraEdit就行。
首先确保UltraEdit的Advanced->Configuration->Search->Regular Expression Engine里,"Perl compatible Regular Expressions"打上了钩。(我不熟悉UE自己定义的Regex的规格,试了好几次都不行,放弃了,换回相对熟悉些的Perl系Regex)
然后Ctrl+R(或者Search->Replace),在Find What里输入
[code]^([^=]+)=([0-9]+)$[/code]
在Replace With里输入
[code]\2=\1[/code]
接着File->Sort->Advanced Sort/Options...,选Numeric Sort,完事。
### 配置 IntelliJ IDEA 使用 ANTLR 要在 IntelliJ IDEA 中配置 ANTLR生成格式化的抽象语法树(AST),可以通过以下方式实现: #### 安装插件 首先,在 IntelliJ IDEA 的设置中安装 **ANTLR v4 Plugin**。此插件提供了对 ANTLR 语法的支持以及便捷的调试功能。 1. 打开 `File -> Settings`。 2. 转到 `Plugins`,搜索 “ANTLR”,并安装官方提供的插件。 3. 完成安装后启 IDE。 #### 创建 ANTLR 文件 创建一个新的 `.g4` 文件作为 ANTLR 文法文件,并定义所需的语法规则。例如,假设要解析简单的算术表达式,则可以定义如下文法规则[^5]: ```antlr grammar Arithmetic; expr: term (('+'|'-') term)*; term: factor (('*'|'/') factor)*; factor: INT | '(' expr ')'; INT : [0-9]+ ; WS : [ \t\r\n]+ -> skip ; ``` 保存该文件后,IntelliJ 将自动检测到它是一个 ANTLR 文法文件,并启用相应的工具支持。 #### 生成解析器代码 右键点击 `.g4` 文件,选择 `Generate Recognizer` 来生成基于所定义文法的解析器和访问者类。这些类将用于实际解析输入字符串并构建 AST 或解析树。 #### 构建和遍历 AST 为了获取格式化后的 AST 输出,需编写一段程序调用生成的解析器,并使用 Listener 或 Visitor 模式来处理节点。下面展示了一个完整的例子,演示如何加载上述文法、解析输入并打印出结构化的 AST 表示形式: ```java import org.antlr.v4.runtime.*; import org.antlr.v4.runtime.tree.*; public class Main { public static void main(String[] args) throws Exception { String input = "3 + 5 * 2"; // 设置 CharStream 输入流 CharStream stream = CharStreams.fromString(input); // 初始化 lexer 和 parser ArithmeticLexer lexer = new ArithmeticLexer(stream); CommonTokenStream tokens = new CommonTokenStream(lexer); ArithmeticParser parser = new ArithmeticParser(tokens); ParseTree tree = parser.expr(); // 解析入口点 System.out.println(tree.toStringTree(parser)); // 格式化输出解析树 } } ``` 以上代码片段展示了如何通过 ANTLR 提供的功能读取一个简单表达式的输入串 `"3 + 5 * 2"`,然后将其转化为一棵解析树,并最终以易读的形式显示出来[^1]。 #### 自定义 AST 处理逻辑 如果希望进一步定制 AST 的表现形式或者执行特定的操作,可以考虑实现自己的 `ParseTreeVisitor` 类型对象。这样可以根据具体需求调整每个节点的行为模式。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值