入门 ANTLR 4
                    
                
            最近因为业务需要,所以开始接触 ANTLR 4 语法解析器,在业务层面需要适配自己业务特征的 DSL 输入框。
调研了几种方向:
| 方案 | 优点 | 缺点 | 
|---|---|---|
纯纯的 string 解析 | 
开发周期短,轻松 | 迭代成本高,容易推翻重做 | 
| Babel 解析 | 基于文本解析成 AST 语法树,通过 AST 来完成业务,迭代轻松 | 需要实现文本分词,浏览器环境使用为动态解析,存在性能损耗 | 
| ANTLR 4 解析 | 定义语法能够静态编译出对应代码,迭代轻松 | 需要学习 ANTLR 4 生态环境 | 
结合业务场景的需要,考虑到未来的迭代可能,所以决定使用 ANTLR 4 解析方案。
安装
package.json 所需依赖:
| 名称 | 版本 | 说明 | 
|---|---|---|
antlr4ng-cli | 
^1.0.7 | 
将 ANTLR 4 编译为 TypeScript 文件的终端执行文件 | 
antlr-format-cli | 
^1.2.1 | 
美化 *.g4 格式 | 
antlr4ng | 
2.0.11 | 
ANTLR 4 在 JavaScript 运行时 | 
antlr4-c3 | 
3.3.7 | 
ANTLR 4 代码补全包 | 
使用
antlr4ng-cli编译需要电脑安装 JDK ,我这里对应的 JDK 版本是 17.0.12。
编译脚本参考DTStack/dt-sql-parser。
仓库使用inquirer包进行多 DSL 编译,因为业务场景,我移除了此功能,只对指定的文件进行编译工作。
解析器
简单做个示例,实现变量比较的功能。
在对应的目录下创建 XXLLexer.g4 词法分析文件和 XXLParser.g4 解析文件。
// XXLLexer.g4
// ...
lexer grammar XXLLexer;
options {
    caseInsensitive= true;
}
EQUAL                  : '==';
AND                    : 'AND';
OR                     : 'OR';
VALUE                  : (LETTER | DIGIT)+;
SPACE                  : [ \t\r\n]+ -> skip;
fragment DIGIT         : [0-9];
fragment LETTER        : [A-Za-z];
// XXLParser.g4
// ...
parser grammar XXLParser;
options {
    tokenVocab= XXLLexer;
    caseInsensitive= true;
    superClass=ParserBase;
}
@header {
import { ParserBase } from '../ParserBase';
}
program
    : singleStatement (AND | OR)* singleStatement* EOF
    ;
singleStatement
    : singleStatement (AND | OR) singleStatement
    | equalStatement
    ;
equalStatement
    : VALUE EQUAL VALUE
    ;
最后支持的效果是:
a == bC1 == Z AND a == b
解析器的类也基于业务做了调整,可以参考 dt-sql-parser 项目。
g4 语法学习请参考Getting Started with ANTLR v4。
语法参考antlr/grammars-v4。
代码补全
可以自己基于解析器实现一个代码补全方法,创建的方法:
import { CandidatesCollection, CodeCompletionCore } from 'antlr4-c3';
// ...
const core = new CodeCompletionCore(parserIns/* 解析器实例 */);
/**
 * 设置需要匹配的规则
 * 比如:singleStatement
 * 则设置:
 * protected preferredRules = new Set([
 *   XXLParser.RULE_singleStatement,
 * ]);
 */
core.preferredRules = this.preferredRules;
// 获得候选列表
const candidates = core.collectCandidates(caretTokenIndex, c3Context);
基于候选列表,可以提供一个体检舒适的 DSL 文本输入框,具备代码补全功能。
到此,算是浅尝了一下 ANTLR 4 ,能感受到的好处就是,当 DSL 产生迭代后,我基本只需要修改 g4 文件,重新编译即可。
非常的舒适~
- 本文链接: https://zongzi531.github.io/2025/02/11/%E5%85%A5%E9%97%A8antlr4/
 - 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!