文章目录
前言
由于最近需要对智能合约源代码进行预处理,将其处理成适用于神经网络(如GRU)的输入形式,需要使用现有的支持代码标记化的工具如ANTLR来对源代码进行预处理。由于是第一次接触这个工具,所以来记录一下该工具的安装和简单使用。
这里智能合约源代码是使用Solidity语言编写的,ANTLR也是支持其他编程语言代码的~
一、ANTLR是什么?
ANTLR(Another Tool for Language Recognition)是一个功能强大的解析器生成器,用于读取、处理、执行和翻译结构化文本或二进制文件。ANTLR可以帮助创建语言解析器、编译器、解释器和分析工具,尤其适合对编程语言或数据格式进行语法分析。
ANTLR的主要功能
- 语法定义: 可以通过ANTLR定义编程语言的语法。ANTLR使用语法规则文件描述语言的词法和语法结构。
- 代码生成: 根据定义的语法规则生成解析器、词法分析器、语法树等代码。支持多种编程语言的代码,包括Java、Python、C++、JavaScript等。
- 语法分析: 生成的解析器可以将输入文本或代码解析成抽象语法树(AST),方便进一步的分析、转换和编译。
- 错误处理: 内置错误处理机制,可以捕捉和报告语法错误,帮助调试和验证输入是否符合语法规则。
ANTLR的组成部分
- 词法分析器: 将输入分解为一系列标记(Tokens),如关键词、标识符、数字、运算符等。
- 解析器: 根据词法分析器生成的标记序列,构建抽象语法树或解析树,描述代码的层次结构和语法关系。
- 抽象语法树: 树状数据结构,描述代码的语法结构。可以通过AST进行代码分析、优化和转换。
- 代码生成器: ANTLR生成的代码可以集成到项目中,生成自定义的编程语言工具。
二、ANTLR4安装
注意:ANTLR安装是需要Java环境的,如果自己电脑中没有Java环境的话需要先安装一下。可以通过命令行窗口(cmd)来检查自己的Java环境是否安装成功。
C:\Users\drh20>java --version
java 17.0.10 2024-01-16 LTS
Java(TM) SE Runtime Environment (build 17.0.10+11-LTS-240)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.10+11-LTS-240, mixed mode, sharing)
1.下载
从官网下载ANTLR4,下载页面如下:
下载后的jar包长这样,可以新建一个目录存放(我是存放在了D:\Software\ANTLR目录下):
2.安装及环境配置
接下来,在D:\Software\ANTLR目录下新建两个文件夹,bat
和test
。
配置CLASSPATH
打开环境变量配置界面(控制面板->系统->高级系统设置->高级->环境变量),选择系统变量,如果自己系统变量中没有CLASSPATH,可以点击“新建”选项进行新建。变量名是CLASSPATH,变量值是刚从官网下载的jar包的存放路径,比如我的就是D:\Software\ANTLR\antlr-4.13.2-complete.jar。然后再点击“新建”按钮添加一个.(需要有这一步,不然后面的话可能会报错)
接下来找到系统变量中的Path并进行修改,双击打开,然后将新建的bat文件路径添加进去。完成之后记得一路点击“确定”,这样就完成环境变量配置啦。
bat文件夹
在bat文件夹下新建两个txt文件,分别为antlr4.txt
和grun.txt
antlr4.txt
中添加如下内容:
java org.antlr.v4.Tool %*
grun.txt
中添加如下内容:
java org.antlr.v4.gui.TestRig %*
然后将两个文件的后缀名改为.bat
。
测试安装是否成功
打开命令行窗口(cmd),输入antlr4
,然后回车,输出结果如下所示。
接着输入命令grun
,回车,如果正常输出的话那证明我们的ANTLR4安装和环境配置成功啦!
三、使用ANTLR4进行一个简单的词法分析示例
为了更直观地感受ANTLR4工具,选取了一个智能合约源代码文件,我们来对它进行词法分析,生成源代码转换后的数值序列。
test.sol
pragma solidity ^0.4.18;
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function Ownable() public {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
contract ERC20Basic {
uint256 public totalSupply;
function balanceOf(address who) public view returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
contract IntermediateWallet is Ownable {
address public wallet;
function IntermediateWallet() public {
wallet = 0x246a8bC2bC20826Ba19D8F7FC5799fF69A79388d;
}
function setWallet(address newWallet) public onlyOwner {
wallet = newWallet;
}
function retrieveTokens(address to, address anotherToken) public onlyOwner {
ERC20Basic alienToken = ERC20Basic(anotherToken);
alienToken.transfer(to, alienToken.balanceOf(this));
}
function () payable public {
wallet.transfer(msg.value);
}
}
1. 获取Solidity语法文件
要解析Solidity代码,首先需要一个描述Solidity语言语法的.g4
文件。可以从Solidity ANTLR Grammar上找到现成的语法文件。下载并保存Solidity.g4
。
利用ANTLR4工具,生成词法分析器(Lexer)和解析器(Parser)目标编程语言的代码,需要指定目标语言,为了确保ANTLR4生成的分析器代码适配我们项目的代码。(若不指定目标语言,会默认生成Java的分析器代码)
在该test目录下打开命令行窗口,输入如下命令:
antlr4 -Dlanguage=Python Solidity.g4
回车,会生成相应的文件。
2. 对test.sol进行词法分析,生成词汇表vocab.json
把上一步生成的SolidityLexer.py和SolidityParser.py放到自己的项目中(此处放在了项目根目录下的myTokensize文件夹中),然后将SolidityLexer
和SolidityParser
作为头文件导进来。其他头文件根据需要自行导入。
from antlr4 import *
from myTokenize.SolidityLexer import SolidityLexer
from myTokenize.SolidityParser import SolidityParser
if __name__ == '__main__':
# 合约源代码路径
file_path = r'.\sourceCode\test.sol'
all_tokens = [] # 存放每个词法标记,关键词、标识符、运算符等
# 打开合约文件
input_stream = FileStream(file_path, encoding='utf-8')
# 创建词法分析器,可能会出现异常
lexer = SolidityLexer(input_stream)
token_stream = CommonTokenStream(lexer)
# 创建解析器
parser = SolidityParser(token_stream)
# 获取词法标记
tokens = lexer.getAllTokens()
all_tokens.extend([token.text for token in tokens])
print(all_tokens)
# 构建全局词汇表,确保每个 token 只出现一次,并按字典序排序
unique_tokens = sorted(set(all_tokens))
vocab = {
token: idx for idx, token in enumerate(unique_tokens)}
# 将全局词汇表保存为 vocab.json 文件
with open('./vocab.json', 'w', encoding='utf-8') as f:
json.dump(vocab, f, ensure_ascii=False, indent=4)
print('词汇表构建完成!')
生成的词汇表vocab.json,源代码被分解为一系列的标记(Token),每个Token对应一个唯一的索引(ID)。
{
"!=": 0,
"(": 1,
")": 2,
",": 3,
".": 4,
"0": 5,
"0.4.18": 6,
"0x246a8bC2bC20826Ba19D8F7FC5799fF69A79388d": 7,
";": 8,
"=": 9,
"==": 10,
"ERC20Basic": 11,
"IntermediateWallet": 12,
"Ownable": 13,
"OwnershipTransferred": 14,
"Transfer": 15,
"^": 16,
"_": 17,
"address": 18,
"alienToken": 19,
"anotherToken": 20,
"balanceOf": 21,
"bool": 22,
"contract": 23,
"event": 24,
"from": 25,
"function": 26,
"indexed": 27,
"is": 28,
"modifier": 29,
"msg": 30,
"newOwner": 31,
"newWallet": 32,
"onlyOwner": 33,
"owner": 34,
"payable": 35,
"pragma": 36,
"previousOwner": 37,
"public": 38,
"require": 39,
"retrieveTokens": 40,
"returns": 41,
"sender": 42,
"setWallet": 43,
"solidity": 44,
"this": 45,
"to": 46,
"totalSupply": 47,
"transfer": 48,
"transferOwnership": 49,
"uint256": 50,
"value": 51,
"view": 52,
"wallet": 53,
"who": 54,
"{": 55,
"}": 56
}
3. 根据构建的词汇表对源代码进行词法标记,并转换为数值序列
from antlr4 import *
from myTokenize.SolidityLexer import SolidityLexer
from myTokenize.SolidityParser import SolidityParser
if __name__ == '__main__':
# 从 vocab.json 文件中加载词汇表
with open('./vocab.json', 'r', encoding='utf-8') as f:
vocab = json.load(f)
file_path = r'.\sourceCode\test.sol'
# 打开合约文件
input_stream = FileStream(file_path, encoding='utf-8')
# 创建词法分析器,可能会出现异常
lexer = SolidityLexer(input_stream)
token_stream = CommonTokenStream(lexer)
# 创建解析器
parser = SolidityParser(token_stream)
# 获取词法标记(关键词、标识符、操作符等)
tokens = lexer.getAllTokens()
# 将标记转换为数值序列 [1,2,3,4,5...]
indexed_tokens = [vocab[token.text] for token in tokens if token.text in vocab]
print(indexed_tokens) # [36, 44, 16, 6, 8, 23, 13, 55, 18, 38, 34, 8, 24, 14...]
总结
本文介绍了ANTLR4工具的下载安装和简单使用,第一次使用ANTLR4,有误的地方欢迎大家指正~