简体   繁体   English

python 解析器找到定义宏的位置

[英]python parser find where macros are defined

I have a test header file named header2.h that only has我有一个名为 header2.h 的测试 header 文件,它只有

#define ford 15

then another header called header1.h which defines然后另一个 header 称为 header1.h 定义

#include "myheader2.h"
#define make ford
#define car_age 10

I;m trying to automate a Python script using the Pyparsing library so I can parse C header files.我正在尝试使用 Pyparsing 库自动执行 Python 脚本,以便我可以解析 C header 文件。 Let's say I want to verify that the define car_age exists then I would parse header1.h and check print car_age does exist using the Python script.假设我想验证 define car_age 是否存在,然后我将解析 header1.h 并使用 Python 脚本检查 print car_age 是否存在。 Let's say I want to verify that the #define make ford exists then I would have to parse header.2 to make sure "ford" exists first.假设我想验证 #define make ford 是否存在,那么我将不得不解析 header.2 以确保“ford”首先存在。

My python scripts works but my problem is that I have several header files that use definitions from other header files so the process gets very cumbersome.我的 python 脚本有效,但我的问题是我有几个 header 文件使用其他 header 文件中的定义,因此该过程变得非常麻烦。

I don't think the pyparsing library has a feature to help my problem.我认为 pyparsing 库没有帮助我解决问题的功能。 I was wondering if there is another Python parsing library or a different tool/software that has a feature that can verify macros are defined maybe in the same header file or in another c header file like in my example?我想知道是否有另一个 Python 解析库或具有可以验证宏定义的功能的不同工具/软件可能在同一个 header 文件或另一个 c header 文件中定义,就像我的示例中一样? Thanks谢谢

The key to this design is to use parse actions to maintain the translation table for all the macros;这个设计的关键是使用解析动作来维护所有宏的翻译表; and dynamically update the pyparsing Forward expression that looks for them and substitute them when they are referenced.并动态更新查找它们的 pyparsing Forward表达式,并在引用它们时替换它们。

Starting with the macroExpander.py example, we add another expression to detect #include 's, a parse action to process them, and a stack to guard against cyclic includes.从 macroExpander.py 示例开始,我们添加了另一个表达式来检测#include ,一个解析操作来处理它们,以及一个堆栈来防止循环包含。

macroInclude = "#include" + pp.quoted_string("include_file_reference").add_parse_action(pp.remove_quotes)

# global stack for include processing (to guard against cyclic includes)
include_stack = []

def process_include(s, l, t):
    filename = t.include_file_reference
    if filename in include_stack:
        raise ValueError(f"cyclic reference to {filename!r}")

    # print(f"processing file {filename!r}")
    include_stack.append(filename)

    resolved_file = resolve_file(filename)
    if resolved_file is not None:
        # searching for matches will update the macros dict
        macroExpander.search_string(resolved_file.read_text())

    # all done with this include file, pop from the stack
    include_stack.pop()
    return " ".join(t)

macroInclude.add_parse_action(process_include)

Add macroInclude to the macroExpander expression:macroInclude添加到 macroExpander 表达式:

# define pattern for scanning through the input string
macroExpander = macroExpr | macroDef | macroInclude

I also added this line so we can comment out sample code and the parser will be smart enough to skip over them:我还添加了这一行,这样我们就可以注释掉示例代码,解析器将足够聪明地跳过它们:

# ignore comments
macroExpander.ignore(pp.c_style_comment)

Here is some test code to create your sample files in a temp directory created using the tempfile module of the stdlib:下面是一些测试代码,用于在使用 stdlib 的 tempfile 模块创建的临时目录中创建示例文件:

from tempfile import TemporaryDirectory
from pathlib import Path
from textwrap import dedent

# header1.h contents
file1 = dedent("""\
/* change to header1.h to see handling of cyclic include */
#include "header2.h"
#define make ford
#define car_age 10
""")

# header2.h contents
file2 = dedent("""\
#define ford 15
""")

# program.c contents
file3 = dedent("""\
#include "header1.h"
#include <stdio.h>

printf("My car is a ", make, " it is ", car_age, " years old!\\n");
""")

# create a temporary dir for header files
with TemporaryDirectory() as tmpdir_str:
    tmpdir = Path(tmpdir_str)

    def resolve_file(fname):
        ret = tmpdir / fname
        if ret.exists():
            return ret
        else:
            return None

    (tmpdir / "header1.h").write_text(file1)
    (tmpdir / "header2.h").write_text(file2)
    (tmpdir / "program.c").write_text(file3)

    expanded = macroExpander.transform_string((tmpdir / "program.c").read_text())
    print(expanded)
    print(macros)

Running this, I get the following:运行这个,我得到以下信息:

#include header1.h
#include <stdio.h>

printf("My car is a ", 15, " it is ", 10, " years old!\n");

{'ford': '15', 'make': '15', 'car_age': '10'}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM