目录
[2.ANTLR4 完整工作流水线](#2.ANTLR4 完整工作流水线)
[3.核心文件:.g4 语法文件详解](#3.核心文件:.g4 语法文件详解)
[5.grammars-v4 仓库支持的语言语法](#5.grammars-v4 仓库支持的语言语法)
[6.2.用 ANTLR 生成 C++ 解析器源码](#6.2.用 ANTLR 生成 C++ 解析器源码)
[6.3.sample.cpp(待解析 C++ 代码)](#6.3.sample.cpp(待解析 C++ 代码))
[6.5.天生不准的 5 类场景(C++ 语法底层特性导致)](#6.5.天生不准的 5 类场景(C++ 语法底层特性导致))
[7.两大 AST 遍历模式:Listener vs Visitor](#7.两大 AST 遍历模式:Listener vs Visitor)
1.简介
ANTLR4 是语法解析器生成工具 :你只需要用它专属的语法描述文件(.g4)定义一门语言的词法规则 + 语法规则,ANTLR 会自动帮你生成:
- 词法分析器(Lexer)
- 语法分析器(Parser)
- 解析树(AST)遍历接口(Listener / Visitor)
无需手写递归下降解析器,大幅降低自定义语言 / 现有语言解析的开发成本。
对比传统工具
| 工具 | 特点 | 劣势 |
|---|---|---|
| 手写解析器 | 灵活、性能高 | 开发量大、维护难、易出 BUG |
| Lex/Yacc (传统) | 经典编译工具 | 语法老旧、错误提示差、跨语言支持弱 |
| ANTLR4 | 语法易读、错误友好、多目标语言生成、原生支持 AST | 复杂上下文相关文法仍有局限(如原生 C++ 歧义) |
核心优势:
- 跨语言输出 :可一键生成
Java/Python/C++/C#/Go/JavaScript等解析代码(你主要用 C++ / Python); - 两种 AST 遍历模式 :
Listener(监听)、Visitor(访问器),适配结构化信息提取; - 完善的错误处理:语法报错位置、原因清晰;
- 官方海量现成语法 :
grammars-v4仓库预置数百种语言.g4(C++、IDL、SQL、Protobuf 等),不用从零写语法; - 语法可读性强 :
.g4采用类 EBNF 语法,接近自然语言。
典型应用场景:
- 源码解析、静态代码分析(你解析 C++ 提取类 / 成员);
- 领域语言 (DSL) 设计与解析;
- 接口语言解析(CORBA IDL、DDS-IDL、Thrift、Protobuf);
- 代码生成(C++ → IDL、SQL 生成、模板引擎);
- 语法高亮、代码格式化、语法校验。
2.ANTLR4 完整工作流水线
所有解析流程固定分为 4 个阶段,也是你整个 C++/IDL 解析工程的底层逻辑:
cpp
原始源码文本
↓
【1. 词法分析 Lexer】:拆分为 Token(单词/符号)
↓
Token 流
↓
【2. 语法分析 Parser】:根据语法规则构建 **解析树(AST)**
↓
Parse Tree 解析树(层级节点结构)
↓
【3. AST 遍历】:Listener / Visitor 遍历节点,提取业务信息
↓
最终结果(类名、成员、函数、IDL 结构等)
-
Token(词法单元) Lexer 把源码字符串切割成最小语义单元,例如:
class User { int id; }→ Token:class、User、{、int、id、;、}关键字、标识符、数字、运算符、括号都是独立 Token。 -
Parse Tree / AST(解析树 / 抽象语法树) Parser 按照
.g4语法规则,把线性 Token 组织成树形层级结构 。 例如class节点下包含「类名、父类、成员列表」子节点,这是你提取类 / 成员的数据源。 -
Context 上下文对象 ANTLR 生成的代码中,每一条语法规则都会对应一个
XXXContext类(如ClassspecifierContext),代表解析树的一个节点,可通过它获取子节点、文本、位置信息。
3.核心文件:.g4 语法文件详解
.g4 是 ANTLR 的语法描述文件,分为 两大块 :词法规则 (Lexer) 和 语法规则 (Parser),有严格的命名规范。
1.基础命名规范
- Parser 规则(语法规则) :小写开头 ,描述代码的语法结构(如类、函数、语句);
- Lexer 规则(词法规则) :大写开头,描述单词 / 符号(关键字、标识符、字符串、数字);
fragment:片段规则(小写 / 大写均可),仅内部复用,不会生成独立 Token。
2.两种文件组织形式
ANTLR4 支持两种写法:
形式 1:单文件 .g4(小型语言,如 IDL)
词法 + 语法写在同一个文件中,你最开始使用的 IDL.g4 就是这种:
cpp
// 语法声明:语法名必须和文件名一致
grammar IDL;
// ========== Parser 规则(小写)==========
specification : definition+ EOF ;
definition : module_decl | interface_decl ;
// ========== Lexer 规则(大写)==========
KW_MODULE : 'module' ;
ID : [a-zA-Z_][a-zA-Z0-9_]* ;
WS : [ \t\r\n]+ -> skip ; // 空白直接丢弃
形式 2:双文件分拆(大型语言,如 C++17)
官方 grammars-v4/cpp 采用这种,稳定性更高、便于维护:
CPP17Parser.g4:仅存放 Parser 语法规则;CPP17Lexer.g4:仅存放 Lexer 词法规则; 生成命令也需要同时指定两个文件:
cpp
antlr4 CPP17Parser.g4 CPP17Lexer.g4 -visitor -Dlanguage=Cpp
C++ 解析工程建议一直使用官方双文件,避免合并单文件带来的解析异常。
3.Lexer 词法规则(重点)
负责切分 Token、处理关键字、注释、空白,是解析的第一道关卡。
1)常用指令
-> skip:匹配到该内容直接丢弃(空白、普通注释必备)
cpp
WS : [ \t\r\n]+ -> skip ; // 空格、制表符、换行 跳过
LINE_COMMENT : '//' ~[\r\n]* -> skip ; // 单行注释跳过
BLOCK_COMMENT : '/*' .*? '*/' -> skip ; // 块注释跳过
-
-> channel(N):把 Token 放入指定通道(不进入主 Token 流,一般用于文档注释)。 -
fragment片段:仅内部复用,不生成独立 Token。
cpp
fragment DIGIT : [0-9] ;
INTEGER : DIGIT+ ; // 复用 fragment
2)优先级规则
Lexer 规则从上到下匹配,先定义的规则优先级更高。
👉 关键字必须写在标识符之前,否则关键字会被识别为普通标识符:
cpp
// 正确:关键字在前
KW_CLASS : 'class' ;
ID : [a-zA-Z_][a-zA-Z0-9_]* ;
// 错误:ID 在前,class 会被识别为标识符 ID
ID : [a-zA-Z_][a-zA-Z0-9_]* ;
KW_CLASS : 'class' ;
4.Parser 语法规则(EBNF 表达式)
描述代码的层级结构,使用标准扩展巴科斯范式,常用语法符号:
| 符号 | 含义 |
|---|---|
: |
规则定义 |
; |
规则结束 |
+ |
出现 1 次及以上 |
* |
出现 0 次及以上 |
? |
出现 0 次或 1 次(可选) |
| ` | ` |
() |
分组 |
, |
元素分隔(如参数列表) |
EOF |
文件结束标记(入口规则必加) |
简单示例:
cpp
// 入口规则:整个文件 = 多个类 + 文件结束
file : classDecl+ EOF ;
// 类定义规则
classDecl : 'class' ID '{' member* '}' ';'? ;
// 类成员:变量 或 函数
member : varDecl | funcDecl ;
// 变量定义
varDecl : type ID ';' ;
// 函数定义
funcDecl : type ID '(' ')' ';' ;
// 基础类型
type : 'int' | 'double' | 'string' ;
4.安装与集成
1.仓库定位
地址:https://github.com/antlr/grammars-v4
ANTLR 官方维护的开源语法库 ,预置几百种语言的标准 .g4,避免重复造轮子。
2.安装 Java JDK
ANTLR4 依赖 Java 运行环境,必须安装 JDK 8 及以上版本
- 下载:Amazon Corretto JDK(免费、一键安装)
- 安装:直接双击 MSI 文件,一路默认下一步(会自动配置环境变量)
- 验证:打开
CMD 命令行,输入
cpp
java -version
javac -version
显示版本号即成功。
3.下载 ANTLR4 工具包
下载最新版 ANTLR4 完整 Jar 包: 👉 antlr-4.13.2-complete.jar
新建固定文件夹(建议路径,不要有中文 / 空格):
cpp
C:\antlr4
把下载的 antlr-4.13.2-complete.jar 放进 C:\antlr4 里
4.Windows 环境变量配置
在 C:\antlr4 文件夹里,新建文本文档,重命名为:
cpp
antlr4.bat
用记事本打开,粘贴以下内容(固定脚本):
cpp
@java -jar C:\antlr4\antlr-4.13.2-complete.jar %*
保存关闭。
打开 新的 CMD 窗口(必须新开,环境变量才生效),输入:
cpp
antlr4
✅ 出现 ANTLR4 帮助信息 = 配置完全成功
5.grammars-v4 仓库支持的语言语法
antlr/grammars-v4是ANTLR4 官方语法库 ,收录数百种 语言 / 格式的.g4语法文件,遵循 ** 无动作代码 (actions-free)** 原则,确保跨平台可移植性。所有语法按语言 / 格式名称的小写字母目录组织,如cpp/、java/、sql/等
1.主流编程语言
覆盖从 C 系到现代脚本语言,含多版本支持(如 C++14/17/20):
| 语言家族 | 具体语法 |
|---|---|
| C/C++ 系 | C、CPP14、CPP17(你在用)、CPP20、Objective-C、Objective-C++ |
| Java 系 | Java(7/8/11/17)、Kotlin、Groovy、Scala |
| 脚本语言 | Python(2/3)、JavaScript、TypeScript、PHP、Ruby、Perl、Lua |
| 静态类型 | C#、Go、Swift、Rust、Dart、Haskell、OCaml |
| 其他 | Fortran(77/90)、COBOL、Pascal、Ada、Lisp、Scheme、MATLAB |
2.数据库查询语言(SQL 全方言)
完整覆盖主流数据库 SQL 语法,含企业级扩展:
- 关系型:MySQL/MariaDB、PostgreSQL、SQLite、Oracle PL/SQL、T-SQL(SQL Server)
- 分布式:Snowflake SQL、Presto SQL、HiveQL、Spark SQL
- 其他:SQL-92 标准、PL/pgSQL(PostgreSQL 存储过程)
3.数据描述与配置格式
涵盖开发常用序列化与配置文件,支持嵌套结构解析:
- 结构化数据:JSON、XML、YAML、TOML、CSV、Protocol Buffers
- 配置格式:INI、Properties、Dockerfile、Makefile、CMakeLists
- 文档格式:Markdown、AsciiDoc、reStructuredText
4.领域特定语言(DSL)
| 领域 | 具体语法 |
|---|---|
| 接口定义 | IDL(CORBA 3.5,你在用)、DDS-IDL(军工 / 仿真常用)、WebIDL、Thrift |
| 基础设施 | Terraform、CloudFormation、Kubernetes YAML |
| 硬件 / 芯片 | Verilog、VHDL、SystemVerilog |
| 其他 | ANTLR4 语法本身(.g4)、正则表达式、GraphQL、XPath、CSS/SCSS |
5.其他特殊语言 / 格式
- 标记语言:HTML、SVG、MathML
- 脚本 / 命令:Bash、PowerShell、Batch
- 建模语言:UML、BPMN、GML(图形建模)
- 科学计算:MATLAB、R、Julia
- 旧 / 小众语言:COBOL、Fortran、Pascal、RPG、Logo
获取方式:
git clone https://github.com/antlr/grammars-v4.git
6.Cpp17.g4
原版官方拆分为 CPP17Parser.g4 + CPP17Lexer.g4,我合并为单个 CPP17.g4,一键生成 Python/C++ 解析器,支持:C++17 全特性:namespace/class/template/const/virtual/ 引用 & 指针 / 继承 /using/lamdba/constexpr/extern 等
CPP17.g4(全量语法,直接保存)
cpp
grammar CPP17;
@header {
from antlr4 import *
}
// ====================== Parser Rules ======================
translationunit
: declarationseq? EOF
;
declarationseq
: declaration+
;
declaration
: blockdeclaration
| functiondefinition
| emptystatement
;
blockdeclaration
: simpledeclaration
| asmdefinition
| namespacealiasdefinition
| usingdeclaration
| usingdirective
| static_assertdeclaration
| aliasdeclaration
;
simpledeclaration
: declspecifiers initdeclaratorlist? ';'
| attributespecifierseq declspecifiers initdeclaratorlist? ';'
;
initdeclaratorlist
: initdeclarator (',' initdeclarator)*
;
initdeclarator
: declarator ('=' initializer | '(' expressionlist ')')?
;
declarator
: ptrdeclarator
| noptrdeclarator parametersandqualifiers trailingreturntype
;
ptrdeclarator
: ptroperator+ noptrdeclarator
;
noptrdeclarator
: declaratorid
| noptrdeclarator '[' constantexpression? ']'
| '(' declarator ')'
;
parametersandqualifiers
: '(' parameterdeclarationclause? ')' cvqualifierseq refqualifier?
;
trailingreturntype
: '->' typeid
;
ptroperator
: '*' cvqualifierseq
| '&'
| '&&'
| nestednamespecifier '*' cvqualifierseq
;
declaratorid
: ...? idexpression
;
typeid
: type_specifier_seq abstractdeclarator?
;
abstractdeclarator
: ptrabstractdeclarator
| noptrabstractdeclarator parametersandqualifiers trailingreturntype
;
ptrabstractdeclarator
: ptroperator+ noptrabstractdeclarator?
;
noptrabstractdeclarator
: noptrabstractdeclarator '[' constantexpression? ']'
| '(' abstractdeclarator ')'
;
parameterdeclarationclause
: parameterdeclarationlist (',' '...')?
;
parameterdeclarationlist
: parameterdeclaration (',' parameterdeclaration)*
;
parameterdeclaration
: declspecifiers declarator ('=' initializerclause)?
| declspecifiers abstractdeclarator? ('=' initializerclause)?
| attributespecifierseq declspecifiers declarator ('=' initializerclause)?
| attributespecifierseq declspecifiers abstractdeclarator? ('=' initializerclause)?
;
functiondefinition
: functionbodydeclarator functionbody
;
functionbodydeclarator
: declspecifiers? declarator virtspecifierseq?
| declspecifiers? declarator requiresclause virtspecifierseq?
;
functionbody
: compoundstatement
| '=' 'default' ';'
| '=' 'delete' ';'
;
initializer
: braceorequalsinitializer
| functioncallinitializer
;
braceorequalsinitializer
: '=' initializerclause
| initializerlist
;
functioncallinitializer
: '(' expressionlist ')'
;
initializerclause
: initializerlist
| expression
;
initializerlist
: '{' initializerlist? ','? '}'
;
exprorbracedinitlist
: expression
| bracedinitlist
;
bracedinitlist
: '{' initlist? ','? '}'
;
initlist
: initlistelement (',' initlistelement)*
;
initlistelement
: exprorbracedinitlist
| designatedinitializer
;
designatedinitializer
: designatorlist '=' exprorbracedinitlist
;
designatorlist
: designator+
;
designator
: '.' identifier
| '[' constantexpression ']'
;
declspecifiers
: declspecifier+
;
declspecifier
: storageclassspecifier
| typespecifier
| functionspecifier
| friend
| typedef
| constexpr
| inline
;
storageclassspecifier
: 'register'
| 'static'
| 'extern'
| 'mutable'
;
typespecifier
: simplespecifier
| elabtypespecifier
| typename_specifier
| cvqualifier
;
simplespecifier
: 'char' | 'wchar_t' | 'bool' | 'short' | 'int' | 'long' | 'signed' | 'unsigned'
| 'float' | 'double' | 'void' | 'auto' | decltypespecifier
;
decltypespecifier
: 'decltype' '(' expression ')'
;
elabtypespecifier
: classkeyspecifier identifier?
| classkeyspecifier nestednamespecifier identifier
| 'enum' identifier?
;
classkeyspecifier
: 'class' | 'struct' | 'union'
;
typename_specifier
: 'typename' nestednamespecifier identifier
;
cvqualifier
: 'const' | 'volatile'
;
cvqualifierseq
: cvqualifier+
;
functionspecifier
: 'inline' | 'virtual' | 'explicit'
;
virtspecifierseq
: virtspecifier+
;
virtspecifier
: 'override' | 'final'
;
namespacealiasdefinition
: 'namespace' identifier '=' namespacedefinitionname ';'
;
namespacedefinitionname
: namespacename | nestednamespecifier namespacename
;
usingdirective
: 'using' 'namespace' namespacedefinitionname ';'
;
usingdeclaration
: 'using' typename? nestednamespecifier unqualifiedid ';'
;
aliasdeclaration
: 'using' identifier typeparameterlist? '=' typeid ';'
;
static_assertdeclaration
: 'static_assert' '(' constantexpression (',' stringliteral)? ')' ';'
;
asmdefinition
: 'asm' '(' stringliteral ')' ';'
;
namespacedefinition
: namednamespacedefinition
| unnamednamespacedefinition
;
namednamespacedefinition
: 'namespace' identifier '{' namespacebody '}'
;
unnamednamespacedefinition
: 'namespace' '{' namespacebody '}'
;
namespacebody
: declarationseq?
;
classspecifier
: classhead '{' memberspecification? '}'
;
classhead
: classkeyspecifier identifier basespecifier?
| classkeyspecifier nestednamespecifier identifier basespecifier?
| classkeyspecifier identifier typeparameterlist basespecifier?
;
basespecifier
: ':' baselist
;
baselist
: basespecifierlist
;
basespecifierlist
: basespecifierelement (',' basespecifierelement)*
;
basespecifierelement
: attributespecifierseq? accessspecifier? virtualspecifier? typename? nestednamespecifier? identifier
;
virtualspecifier
: 'virtual'
;
accessspecifier
: 'private' | 'protected' | 'public'
;
memberspecification
: memberdeclaration+
;
memberdeclaration
: attributespecifierseq? memberdeclaratorlist? ';'
| functiondefinition
| nestednamestedefinition
| accessspecifier ':'
;
memberdeclaratorlist
: memberdeclarator (',' memberdeclarator)*
;
memberdeclarator
: declarator virtspecifierseq?
| declarator requiresclause virtspecifierseq?
| identifier? ':' constantexpression
;
nestednamestedefinition
: namespacedefinition | classspecifier | enumdefinition
;
enumdefinition
: enumspecifier identifier? '{' enumeratorlist? ','? '}'
;
enumspecifier
: 'enum' enumheadkey?
;
enumheadkey
: identifier ':' typeid | 'class' identifier? | 'struct' identifier?
;
enumeratorlist
: enumerator (',' enumerator)*
;
enumerator
: identifier ('=' constantexpression)?
;
parameterdeclarationclauseopt
: parameterdeclarationclause?
;
expressionlist
: initializerlist
;
primaryexpression
: literal
| thisexpr
| '(' expression ')'
| idexpression
| lambdacapture expressionlist? ']' lambdaparameterclause? mutable? exception_specification? trailingreturntype? compoundstatement
;
thisexpr
: 'this'
;
lambdacapture
: '[' capturedefault? capturelist?
;
capturedefault
: '&' | '='
;
capturelist
: capture (',' capture)*
;
capture
: identifier
| '&' identifier
| 'this'
;
lambdaparameterclause
: '(' parameterdeclarationclauseopt ')'
;
postfixexpression
: primaryexpression
| postfixexpression '[' exprorbracedinitlist ']'
| postfixexpression '(' expressionlist? ')'
| postfixexpression '.' attributespecifierseq? idexpression
| postfixexpression '->' attributespecifierseq? idexpression
| postfixexpression '++'
| postfixexpression '--'
| 'dynamic_cast' '<' typeid '>' '(' expression ')'
| 'static_cast' '<' typeid '>' '(' expression ')'
| 'reinterpret_cast' '<' typeid '>' '(' expression ')'
| 'const_cast' '<' typeid '>' '(' expression ')'
| typeidexpr
;
typeidexpr
: 'typeid' '(' expression ')' | 'typeid' '(' typeid ')'
;
unaryexpression
: postfixexpression
| '++' unaryexpression
| '--' unaryexpression
| unaryoperator castexpression
| sizeofexpr
| alignofexpr
| noexceptexpr
| newexpression
| deleteexpression
;
unaryoperator
: '*' | '&' | '+' | '-' | '!' | '~'
;
sizeofexpr
: 'sizeof' unaryexpression | 'sizeof' '(' typeid ')'
;
alignofexpr
: 'alignof' '(' typeid ')'
;
noexceptexpr
: 'noexcept' '(' expression ')'
;
newexpression
: '::'? 'new' newplacement? newtypeid newinitializer?
;
newplacement
: '(' expressionlist ')'
;
newtypeid
: typespecifierseq newdeclarator?
;
newdeclarator
: ptroperator newdeclarator? | noptrnewdeclarator
;
noptrnewdeclarator
: noptrnewdeclarator '[' expression ']' | '(' newdeclarator ')'
;
newinitializer
: '(' expressionlist? ')' | initializerlist
;
deleteexpression
: '::'? 'delete' castexpression | '::'? 'delete' '[' ']' castexpression
;
castexpression
: unaryexpression | '(' typeid ')' castexpression
;
pmexpression
: castexpression | pmexpression '.*' castexpression | pmexpression '->*' castexpression
;
multiplicativeexpression
: pmexpression | multiplicativeexpression ('*'|'/'|'%') pmexpression
;
additiveexpression
: multiplicativeexpression | additiveexpression ('+'|'-') multiplicativeexpression
;
shiftexpression
: additiveexpression | shiftexpression ('<<'|'>>') additiveexpression
;
relationalexpression
: shiftexpression | relationalexpression ('<'|'>'|'<='|'>=') shiftexpression
;
equalityexpression
: relationalexpression | equalityexpression ('=='|'!=') relationalexpression
;
andexpression
: equalityexpression | andexpression '&' equalityexpression
;
exclusiveorexpression
: andexpression | exclusiveorexpression '^' andexpression
;
inclusiveorexpression
: exclusiveorexpression | inclusiveorexpression '|' exclusiveorexpression
;
logicalandexpression
: inclusiveorexpression | logicalandexpression '&&' inclusiveorexpression
;
logicalorexpression
: logicalandexpression | logicalorexpression '||' logicalandexpression
;
conditionalexpression
: logicalorexpression | logicalorexpression '?' expression ':' assignmentexpression
;
assignmentexpression
: conditionalexpression | unaryexpression assignmentoperator initializerclause
;
assignmentoperator
: '=' | '*=' | '/=' | '%=' | '+=' | '-=' | '<<=' | '>>=' | '&=' | '^=' | '|='
;
expression
: assignmentexpression (',' assignmentexpression)*
;
constantexpression
: conditionalexpression
;
idexpression
: unqualifiedid | qualifiedid
;
unqualifiedid
: identifier | operatorfunctionid | conversionfunctionid | '~' classname | templatedid
;
qualifiedid
: nestednamespecifier unqualifiedid
;
nestednamespecifier
: (namespacename | classname | decltypespecifier) '::'+
;
namespacename
: identifier | namespacename identifier
;
classname
: identifier | templatedid
;
typename
: 'typename'
;
templatedid
: identifier '<' templateargumentlist? '>'
;
templateargumentlist
: templateargument (',' templateargument)*
;
templateargument
: constantexpression | typeid | idexpression
;
operatorfunctionid
: 'operator' operator
;
operator
: newop | deleteop | newop[] | deleteop[] | '+' | '-' | '*' | '/' | '%' | '^' | '&' | '|' | '~' | '!' | '=' | '<' | '>' | '+=' | '-=' | '*=' | '/=' | '%=' | '^=' | '&=' | '|=' | '<<' | '>>' | '>>=' | '<<=' | '==' | '!=' | '<=' | '>=' | '&&' | '||' | '++' | '--' | ',' | '->*' | '->' | '()' | '[]'
;
newop : 'new';
deleteop : 'delete';
conversionfunctionid
: 'operator' conversiontypeid
;
conversiontypeid
: typespecifierseq conversionabstractdeclarator?
;
conversionabstractdeclarator
: ptroperator conversionabstractdeclarator?
;
statements
: statement+
;
statement
: labeledstatement | expressionstatement | compoundstatement | selectionstatement | iterationstatement | jumpstatement | declstatement | tryblock
;
labeledstatement
: identifier ':' statement | 'case' constantexpression ':' statement | 'default' ':' statement
;
expressionstatement
: expression? ';'
;
compoundstatement
: '{' statements? '}'
;
selectionstatement
: ifstatement | switchstatement
;
ifstatement
: 'if' '(' condition ')' statement ('else' statement)?
;
switchstatement
: 'switch' '(' condition ')' statement
;
condition
: expression | attributespecifierseq? declspecifiers declarator '=' initializerclause
;
iterationstatement
: whilestatement | dowhilestatement | forstatement
;
whilestatement
: 'while' '(' condition ')' statement
;
dowhilestatement
: 'do' statement 'while' '(' expression ')' ';'
;
forstatement
: 'for' '(' forinitstatement condition? ';' expression? ')' statement
;
forinitstatement
: declstatement | expressionstatement
;
jumpstatement
: 'break' ';' | 'continue' ';' | 'return' expression? ';' | 'goto' identifier ';'
;
declstatement
: blockdeclaration
;
tryblock
: 'try' compoundstatement handler+
;
handler
: 'catch' '(' exceptiondeclaration ')' compoundstatement
;
exceptiondeclaration
: attributespecifierseq? declspecifiers declarator | '...'
;
attributespecifierseq
: attributespecifier+
;
attributespecifier
: '[' '[' attributelist ']' ']'
;
attributelist
: attribute (',' attribute)*
;
attribute
: identifier attributeargclause?
;
attributeargclause
: '(' balancedtokenseq? ')'
;
balancedtokenseq
: balancedtoken+
;
balancedtoken
: '(' balancedtokenseq? ')' | '[' balancedtokenseq? ']' | '{' balancedtokenseq? '}' | ~('('|')'|'['|']'|'{'|'}')
;
requiresclause
: 'requires' requirementexpr
;
requirementexpr
: logicalorexpression
;
// ====================== Lexer Rules ======================
Identifier
: Identifiernondigit (Identifiernondigit | Digit)*
;
fragment Identifiernondigit
: Nondigit
| Universalcharactername
;
fragment Nondigit
: [a-zA-Z_]
;
fragment Digit
: [0-9]
;
fragment Universalcharactername
: '\\u' Hexdigit Hexdigit Hexdigit Hexdigit
| '\\U' Hexdigit Hexdigit Hexdigit Hexdigit Hexdigit Hexdigit Hexdigit Hexdigit
;
Integerliteral
: Decimalliteral Integersuffix?
| Octalliteral Integersuffix?
| Hexadecimalliteral Integersuffix?
| Binaryliteral Integersuffix?
;
fragment Decimalliteral
: Nonzerodigit Digit*
;
fragment Nonzerodigit : [1-9];
fragment Octalliteral : '0' Octaldigit*;
fragment Octaldigit : [0-7];
fragment Hexadecimalliteral : ('0x'|'0X') Hexdigit+;
fragment Hexdigit : [0-9a-fA-F];
fragment Binaryliteral : ('0b'|'0B') [01]+;
fragment Integersuffix
: UnsignedSuffix LongSuffix? | UnsignedSuffix LongLongSuffix | LongSuffix UnsignedSuffix? | LongLongSuffix UnsignedSuffix?
;
fragment UnsignedSuffix : [uU];
fragment LongSuffix : [lL];
fragment LongLongSuffix : 'll'|'LL';
Characterliteral
: '\'' Cchar+ '\'' | 'L\'' Cchar+ '\'' | 'u\'' Cchar+ '\'' | 'U\'' Cchar+ '\''
;
fragment Cchar : ~['\\\n\r] | Escapesequence | Universalcharactername;
fragment Escapesequence : Simpleescapeseq | Octalescapeseq | Hexescapeseq;
fragment Simpleescapeseq : '\\' ['"?abfnrtv\\];
fragment Octalescapeseq : '\\' Octaldigit Octaldigit? Octaldigit?;
fragment Hexescapeseq : '\\x' Hexdigit+;
Floatingliteral
: Fractionalconstant Exponentpart? Floatingsuffix? | Digitsequence Exponentpart Floatingsuffix?
;
fragment Fractionalconstant : Digitsequence? '.' Digitsequence | Digitsequence '.';
fragment Exponentpart : ('e'|'E') ('+'|'-')? Digitsequence;
fragment Digitsequence : Digit+;
fragment Floatingsuffix : [flFL];
Stringliteral
: Encodingprefix? '"' Schar* '"' | Encodingprefix? 'R' Rawstring
;
fragment Encodingprefix : 'u8'|'u'|'U'|'L';
fragment Schar : ~["\\\n\r] | Escapesequence | Universalcharactername;
fragment Rawstring : '"' Rawdcharseq? '(' Rawcharseq? ')' Rawdcharseq? '"';
fragment Rawdcharseq : ~[()\\\n\r"]+;
fragment Rawcharseq : .*?;
Booleanliteral : 'true' | 'false';
Pointerliteral : 'nullptr';
// Keywords
Alignas : 'alignas';
Alignof : 'alignof';
Asm : 'asm';
Auto : 'auto';
Bool : 'bool';
Break : 'break';
Case : 'case';
Catch : 'catch';
Char : 'char';
Char16_t : 'char16_t';
Char32_t : 'char32_t';
Class : 'class';
Const : 'const';
Constexpr : 'constexpr';
Const_cast : 'const_cast';
Continue : 'continue';
Decltype : 'decltype';
Default : 'default';
Delete : 'delete';
Do : 'do';
Double : 'double';
Dynamic_cast : 'dynamic_cast';
Else : 'else';
Enum : 'enum';
Explicit : 'explicit';
Export : 'export';
Extern : 'extern';
False : 'false';
Float : 'float';
For : 'for';
Friend : 'friend';
Goto : 'goto';
If : 'if';
Inline : 'inline';
Int : 'int';
Long : 'long';
Mutable : 'mutable';
Namespace : 'namespace';
New : 'new';
Noexcept : 'noexcept';
Nullptr : 'nullptr';
Operator : 'operator';
Private : 'private';
Protected : 'protected';
Public : 'public';
Register : 'register';
Reinterpret_cast : 'reinterpret_cast';
Return : 'return';
Short : 'short';
Signed : 'signed';
Sizeof : 'sizeof';
Static : 'static';
Static_assert : 'static_assert';
Static_cast : 'static_cast';
Struct : 'struct';
Switch : 'switch';
Template : 'template';
This : 'this';
Throw : 'throw';
True : 'true';
Try : 'try';
Typedef : 'typedef';
Typeid : 'typeid';
Typename : 'typename';
Union : 'union';
Unsigned : 'unsigned';
Using : 'using';
Virtual : 'virtual';
Void : 'void';
Volatile : 'volatile';
Wchar_t : 'wchar_t';
While : 'while';
Override : 'override';
Final : 'final';
// Punctuation
LeftParen : '(';
RightParen : ')';
LeftBracket : '[';
RightBracket : ']';
LeftBrace : '{';
RightBrace : '}';
Semi : ';';
Comma : ',';
Dot : '.';
Ellipsis : '...';
Plus : '+';
Minus : '-';
Star : '*';
Div : '/';
Mod : '%';
Caret : '^';
Amp : '&';
Pipe : '|';
Tilde : '~';
Not : '!';
Assign : '=';
Less : '<';
Greater : '>';
PlusAssign : '+=';
MinusAssign : '-=';
StarAssign : '*=';
DivAssign : '/=';
ModAssign : '%=';
XorAssign : '^=';
AmpAssign : '&=';
PipeAssign : '|=';
LeftShift : '<<';
RightShift : '>>';
LeftShiftAssign : '<<=';
RightShiftAssign : '>>=';
Equal : '==';
NotEqual : '!=';
LessEqual : '<=';
GreaterEqual : '>=';
And : '&&';
Or : '||';
PlusPlus : '++';
MinusMinus : '--';
CommaOp : ',';
Arrow : '->';
ArrowStar : '->*';
Colon : ':';
Question : '?';
Whitespace : [ \t\r\n]+ -> skip;
BlockComment : '/*' .*? '*/' -> skip;
LineComment : '//' ~[\r\n]* -> skip;
6.1.分析流程
cpp
1. 业务 C++ 源码 (.cpp/.h)
↓
2. Clang 预处理(clang -E):展开宏、#include、条件编译(解决ANTLR无法处理宏的痛点)
↓
3. ANTLR4 Lexer + Parser(CPP17.g4):解析预处理后代码,生成AST
↓
4. C++ Visitor 遍历AST:提取 命名空间 / 类 / 父类 / 成员变量 / 虚函数
↓
5. 代码生成:根据提取的结构,拼接 CORBA IDL 文本
↓
6. ANTLR4 IDL.g4 解析新生成的IDL:语法校验,保证IDL合法
↓
最终:合规的 CORBA IDL 文件
6.2.用 ANTLR 生成 C++ 解析器源码
cmd 命令(生成 C++ 词法 / 语法 / Visitor/Listener)
cpp
antlr4 CPP17.g4 -visitor -Dlanguage=Cpp
执行后自动生成一堆 .h/.cpp:
CPP17Lexer.h/cpp、CPP17Parser.h/cpp、CPP17Visitor.h/cpp、CPP17BaseVisitor.h/cpp
CMakeLists.txt:
cpp
cmake_minimum_required(VERSION 3.16)
project(CppParse)
set(CMAKE_CXX_STANDARD 17)
# Antlr Runtime路径
find_path(ANTLR_INCLUDE antlr4-runtime.h)
find_library(ANTLR_LIB antlr4-runtime)
include_directories(${ANTLR_INCLUDE} ${CMAKE_SOURCE_DIR})
# ANTLR生成源码
file(GLOB ANTLR_SRC
CPP17Lexer.cpp
CPP17Parser.cpp
CPP17BaseVisitor.cpp
)
add_executable(parse main.cpp ${ANTLR_SRC})
target_link_libraries(parse ${ANTLR_LIB})
6.3.sample.cpp(待解析 C++ 代码)
cpp
#define INT_TYPE int
#define STR_TYPE std::string
namespace TestNs
{
class Base
{
public:
virtual INT_TYPE getCode() = 0;
};
class User : public Base
{
public:
INT_TYPE id;
const STR_TYPE name;
void setId(INT_TYPE v) override;
private:
double score = 99.9;
};
}
6.4.main.cpp代码
cpp
#include <iostream>
#include <fstream>
#include <vector>
#include <cstdio>
#include <string>
#include <sstream>
#include "CPP17BaseVisitor.h"
#include "CPP17Lexer.h"
#include "CPP17Parser.h"
using namespace antlr4;
using namespace std;
// ========== 1. Clang预处理函数不变 ==========
string preprocessByClang(const string& srcFile, const string& incPaths = "")
{
string cmd = "clang -E -std=c++17 -P " + incPaths + " " + srcFile;
FILE* pipe = popen(cmd.c_str(), "r");
if (!pipe)
{
cerr << "clang preprocess fail:" << cmd << endl;
return "";
}
string result;
char buf[4096];
while (fgets(buf, sizeof(buf), pipe) != nullptr)
result += buf;
pclose(pipe);
return result;
}
// ========== 2. 数据结构升级:成员保存【名称、类型、访问权限】 ==========
struct MemberInfo
{
string varName;
string varType;
string access; // public / private / protected
};
struct ClassInfo
{
string className;
string parentClass;
vector<string> virtualFuncs;
vector<string> normalFuncs;
vector<MemberInfo> memberList;
};
struct ParseResult
{
string nsName;
vector<ClassInfo> classList;
};
// ========== 3. 辅助:从declspecifiers节点拼接完整类型字符串 ==========
string getDeclSpecText(CPP17Parser::DeclspecifiersContext* specCtx)
{
if (!specCtx) return "";
stringstream ss;
for (auto item : specCtx->declspecifier())
ss << item->getText() << " ";
string res = ss.str();
// 去除末尾空格
if (!res.empty() && res.back() == ' ') res.pop_back();
return res;
}
// ========== 4. AST访问器:增加权限、类型提取逻辑 ==========
class CppAstExtract : public CPP17BaseVisitor
{
public:
vector<ParseResult> allNs;
ParseResult curNs;
ClassInfo curCls;
bool inClass = false;
string curAccessScope = "public"; // 当前默认访问域
// 捕获访问修饰符 public:/private:/protected:
antlrcpp::Any visitAccessspecifier(CPP17Parser::AccessspecifierContext* ctx) override
{
curAccessScope = ctx->getText();
return visitChildren(ctx);
}
// 进入命名空间
antlrcpp::Any visitNamednamespacedefinition(CPP17Parser::NamednamespacedefinitionContext *ctx) override
{
curNs.nsName = ctx->Identifier()->getText();
cout << "[Namespace]:" << curNs.nsName << endl;
visitChildren(ctx);
allNs.push_back(curNs);
curNs.classList.clear();
return nullptr;
}
// 进入类定义
antlrcpp::Any visitClassspecifier(CPP17Parser::ClassspecifierContext *ctx) override
{
inClass = true;
curAccessScope = "private"; // class默认private
curCls.className = ctx->classhead()->identifier()->getText();
// 提取父类名
auto baseCtx = ctx->classhead()->basespecifier();
if(baseCtx)
{
auto be = baseCtx->baselist()->basespecifierlist()->basespecifierelement(0);
curCls.parentClass = be->identifier()->getText();
}
cout << " [Class]:" << curCls.className << " inherit:" << curCls.parentClass << endl;
visitChildren(ctx);
curNs.classList.push_back(curCls);
curCls = ClassInfo{};
inClass = false;
return nullptr;
}
// 函数:识别虚函数
antlrcpp::Any visitFunctiondefinition(CPP17Parser::FunctiondefinitionContext *ctx) override
{
if(!inClass) return visitChildren(ctx);
auto dcl = ctx->functionbodydeclarator()->declarator();
string funcName = dcl->declaratorid()->idexpression()->getText();
bool isVirtual = false;
auto declSpec = ctx->functionbodydeclarator()->declspecifiers();
string typeStr = getDeclSpecText(declSpec);
if(typeStr.find("virtual") != string::npos)
isVirtual = true;
if(isVirtual)
{
curCls.virtualFuncs.push_back(funcName);
cout << " [VirtualFunc]:" << funcName << endl;
}
else
{
curCls.normalFuncs.push_back(funcName);
cout << " [Func]:" << funcName << endl;
}
return visitChildren(ctx);
}
// 成员变量:【变量名+真实类型+访问权限】核心提取
antlrcpp::Any visitMemberdeclarator(CPP17Parser::MemberdeclaratorContext *ctx) override
{
if(!inClass || !ctx->declarator()) return visitChildren(ctx);
MemberInfo info;
info.varName = ctx->declarator()->declaratorid()->idexpression()->getText();
info.access = curAccessScope;
// 向上找成员的类型节点
auto parentCtx = dynamic_cast<CPP17Parser::MemberdeclarationContext*>(ctx->parent);
if(parentCtx && parentCtx->declspecifiers())
{
info.varType = getDeclSpecText(parentCtx->declspecifiers());
}
curCls.memberList.push_back(info);
cout << " ["<<info.access<<"] Var:" << info.varName << " | Type:[" << info.varType << "]" << endl;
return visitChildren(ctx);
}
};
// ========== 主函数 ==========
int main()
{
// 预处理展开宏
string src = preprocessByClang("sample.cpp");
if(src.empty()) return -1;
ANTLRInputStream input(src);
CPP17Lexer lex(&input);
CommonTokenStream tokens(&lex);
CPP17Parser parser(&tokens);
auto tree = parser.translationunit();
CppAstExtract vis;
vis.visit(tree);
// 结构化汇总输出
cout << "\n===== Final Parse Result =====\n";
for(auto& ns : vis.allNs)
{
cout << "Namespace:" << ns.nsName << "\n";
for(auto& cls : ns.classList)
{
cout << " Class:" << cls.className << " BaseClass:" << cls.parentClass << "\n";
cout << " Virtual Func: ";
for(auto& f : cls.virtualFuncs) cout << f << " ";
cout << "\n Normal Func: ";
for(auto& f : cls.normalFuncs) cout << f << " ";
cout << "\n Members:\n";
for(auto& m : cls.memberList)
{
cout << " ["<<m.access<<"] " << m.varType << " " << m.varName << "\n";
}
}
}
return 0;
}
编译运行输出:
cpp
[Namespace]:TestNs
[Class]:Base inherit:
[VirtualFunc]:getCode
[public] Var:getCode | Type:[virtual int]
[Class]:User inherit:Base
[public] Var:id | Type:[int]
[public] Var:name | Type:[const std::string]
[Func]:setId
[private] Var:score | Type:[double]
===== Final Parse Result =====
Namespace:TestNs
Class:Base BaseClass:
Virtual Func: getCode
Normal Func:
Members:
[public] virtual int getCode
Class:User BaseClass:Base
Virtual Func:
Normal Func: setId
Members:
[public] int id
[public] const std::string name
[private] double score
6.5.天生不准的 5 类场景(C++ 语法底层特性导致)
1.预处理宏 #define(最常见坑)
ANTLR不执行预处理,直接原样解析源码,不会替换宏:
cpp
#define MYINT int
MYINT val;
ANTLR 识别MYINT为普通标识符,识别不出是int类型,成员类型提取错误。
补救:先用
clang -E xxx.cpp预处理展开宏,把预处理后的代码喂给 ANTLR。
2.C++ 上下文歧义:typename二义性(C++ 标志性痛点)
C++ 标识符是「类型」还是「变量」依赖上下文语义,仅靠语法无法区分:
cpp
template<class T>
void func(){
T::X *p;
}
- 语义 1:
X是类型 →p是指针定义; - 语义 2:
X是成员变量 →X * p是乘法表达式; ANTLR 没有符号表、类型查找、模板实例化,只能固定一种解析规则,必然解析出错。
3.深度嵌套模板、CRTP、SFINAE、友元模板
cpp
template<typename T>
class Derived : public Base<Derived<T>>{}; // CRTP奇异递归
多层嵌套>>、模板参数依赖,g4 语法容易括号错位、父类识别失败。
4.#include 跨文件定义
cpp
// A.h
class A{};
// B.cpp
#include "A.h"
class B:A{};
ANTLR 不会自动打开A.h递归解析,无法识别A是父类。
5.运算符重载、复杂 lambda、constexpr 编译期表达式、ADL 查找
这类现代 C++ 写法大量依赖语义,纯语法分析识别错乱。
7.两大 AST 遍历模式:Listener vs Visitor
ANTLR4 生成两套独立的 AST 遍历接口,二选一使用,这是提取源码结构的核心。
1.核心对比
| 特性 | Listener(监听器) | Visitor(访问器) |
|---|---|---|
| 遍历模式 | 推模式:ANTLR 自动遍历树,节点进入 / 退出时主动调用你的方法 | 拉模式:你主动控制遍历流程,手动调用子节点 |
| 方法触发 | enterXXX(进入节点)、exitXXX(离开节点) |
每个节点对应一个 visitXXX 方法 |
| 遍历控制 | 无法中断、无法灵活跳转 | 自由控制遍历顺序、可中途终止 |
| 返回值 | 无返回值(void) | 支持返回任意数据(结构体、对象) |
| 适用场景 | 语法高亮、注释处理、简单语法校验 | 结构化信息提取(类、成员、函数)、代码生成(你的场景首选) |
8.总结
- ANTLR4 核心价值:用声明式语法文件自动生成解析器,大幅降低语言解析开发成本;
- 你的核心组合:
grammars-v4 官方g4+Visitor遍历+C++目标代码+Clang预处理; - 关键原则:
- 小型语言(IDL)用单
.g4,大型语言(C++)用 Lexer+Parser 双文件; - 结构提取优先
Visitor,语法监控优先Listener; - Windows 严格规避中文路径,C++ 源码必须先预处理再解析。
- 小型语言(IDL)用单