You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Ply(Lex/Yacc)仅解析C++宏调用并忽略其他代码?

实现仅解析C++宏调用的Ply解析器

1. 词法分析器(Lexer)调整

核心思路是优先识别需要处理的宏、字符串、注释,其余所有内容标记为可忽略token,避免触发语法错误。

import ply.lex as lex

# 定义token列表
tokens = [
    'MACRO',
    'LPAREN',
    'RPAREN',
    'COMMA',
    'STRING',
    'IGNORED'
]

# 忽略空格、制表符、换行
t_ignore = ' \t\n'

# 匹配C++单行注释
def t_COMMENT(t):
    r'//.*'
    pass  # 直接忽略

# 匹配C++多行注释
def t_MULTILINE_COMMENT(t):
    r'/\*(.|\n)*?\*/'
    pass  # 直接忽略

# 匹配字符串常量(避免把字符串内的大写下划线误判为宏)
def t_STRING(t):
    r'"([^"\\]|\\.)*"'
    pass  # 字符串内容无需处理,直接忽略

# 匹配宏名:仅大写字母和下划线开头,后续只能是大写字母和下划线
def t_MACRO(t):
    r'[A-Z_][A-Z_]*'
    return t  # 返回宏token供解析器处理

# 匹配括号和逗号
t_LPAREN = r'\('
t_RPAREN = r'\)'
t_COMMA = r','

# 匹配所有其他字符,标记为可忽略
def t_IGNORED(t):
    r'.'
    return t

# 错误处理:跳过非法字符,继续解析
def t_error(t):
    t.skip(1)

# 构建lexer
lexer = lex.lex()

2. 语法分析器(Parser)调整

语法规则设计为允许任意内容存在,仅捕获宏调用并提取参数,其余内容直接忽略,不会抛出语法错误。

import ply.yacc as yacc

# 存储捕获到的宏调用,格式:(宏名, [参数列表])
macro_calls = []

# 顶层规则:整个程序由任意数量的语句组成
def p_program(p):
    '''program : statement*'''
    pass

# 语句可以是宏调用,也可以是忽略的内容
def p_statement(p):
    '''statement : macro_call
                 | ignored'''
    pass

# 匹配宏调用:宏名 + 括号 + 参数列表
def p_macro_call(p):
    '''macro_call : MACRO LPAREN argument_list RPAREN'''
    macro_name = p[1]
    arguments = p[3] if p[3] else []
    macro_calls.append( (macro_name, arguments) )
    print(f"捕获宏调用:{macro_name},参数:{arguments}")

# 处理参数列表:空列表或多个参数
def p_argument_list(p):
    '''argument_list : argument
                     | argument_list COMMA argument'''
    if len(p) == 2:
        p[0] = [p[1]]
    else:
        p[0] = p[1] + [p[3]]

# 处理单个参数:简化处理,若需支持嵌套括号可扩展此规则
def p_argument(p):
    '''argument : IGNORED
                | MACRO
                | STRING'''
    p[0] = p[1]

# 处理空参数列表
def p_argument_list_empty(p):
    '''argument_list : '''
    p[0] = []

# 忽略任意数量的IGNORED token
def p_ignored(p):
    '''ignored : IGNORED+'''
    pass

# 错误处理:跳过错误token,继续解析
def p_error(p):
    if p:
        parser.errok()
    else:
        pass

# 构建parser
parser = yacc.yacc()

3. 使用示例

# 测试代码
test_code = '''
#include <iostream>
using namespace std;

#define LOG_INFO(msg) cout << msg << endl
#define ADD(a, b) (a + b)

int main() {
    int x = 5;
    float y = 3.14;
    LOG_INFO("Hello World");
    int result = ADD(x, y);
    // 注释里的MACRO_TEST不会被识别
    /* 多行注释里的MACRO_IGNORE也不会被识别 */
    return 0;
}
'''

# 解析代码
parser.parse(test_code)

# 输出捕获到的宏调用
print("\n最终捕获的宏调用:")
for macro in macro_calls:
    print(f"{macro[0]}: {macro[1]}")

关键说明

  • 优先级处理:词法分析器中注释、字符串的匹配优先级高于宏,避免误识别注释或字符串内的内容。
  • 错误抑制:通过IGNORED token和p_error函数,让解析器跳过所有非宏调用内容,不会因int、float等关键字或变量抛出语法错误。
  • 参数扩展:若需支持参数内的嵌套括号(如MACRO(func(a,b), c)),需扩展p_argument规则,添加括号嵌套匹配逻辑,避免把括号内的逗号当成参数分隔符。

内容的提问来源于stack exchange,提问作者Ricky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 02:37:02