ANTLR4: CORBA IDL、C++ 语法文件分析利器

目录

1.简介

[2.ANTLR4 完整工作流水线](#2.ANTLR4 完整工作流水线)

[3.核心文件:.g4 语法文件详解](#3.核心文件:.g4 语法文件详解)

4.安装与集成

[5.grammars-v4 仓库支持的语言语法](#5.grammars-v4 仓库支持的语言语法)

6.Cpp17.g4

6.1.分析流程

[6.2.用 ANTLR 生成 C++ 解析器源码](#6.2.用 ANTLR 生成 C++ 解析器源码)

[6.3.sample.cpp(待解析 C++ 代码)](#6.3.sample.cpp(待解析 C++ 代码))

6.4.main.cpp代码

[6.5.天生不准的 5 类场景(C++ 语法底层特性导致)](#6.5.天生不准的 5 类场景(C++ 语法底层特性导致))

[7.两大 AST 遍历模式:Listener vs Visitor](#7.两大 AST 遍历模式:Listener vs Visitor)

8.总结


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:classUser{intid;} 关键字、标识符、数字、运算符、括号都是独立 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 采用这种,稳定性更高、便于维护

  1. CPP17Parser.g4:仅存放 Parser 语法规则
  2. 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-v4ANTLR4 官方语法库 ,收录数百种 语言 / 格式的.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++ 源码必须先预处理再解析。
相关推荐
子非衣1 小时前
Java使用Aspose进行Word转PDF时异常卡主问题
java·pdf·word
此生决int1 小时前
Java面向对象进阶精讲:抽象类、接口、内部类与Object类万字详解
java
阿维的博客日记1 小时前
‘version‘ must be a constant version but is ‘${revision}‘
java·spring boot·后端
Mortalbreeze1 小时前
C++11 ---- 引用折叠、完美转发、可变模板参数、emplace系列接口
开发语言·c++
星恒随风1 小时前
C++ 内存管理详解:从内存分区、malloc/free 到 new/delete
开发语言·c++·笔记·学习
object not found1 小时前
Node.js fs 常用 API 整理:node:fs/promises、node:fs、fs 到底怎么用
开发语言·前端·javascript
C+++Python1 小时前
C++ 常量全面讲解
java·开发语言·c++
江屿风1 小时前
C++图论基础拓扑排序经典OJ题流食般投喂
开发语言·c++·笔记·算法·图论