python环境做C语言分析-pycparser的使用方法(2)

这篇文章根据上一篇的内容做补充:python环境做C语言分析-pycparser的使用方法(1)

这里介绍简单的pycparser使用方法,提供一种 遍历 抽象语法树AST 节点c_ast.py的方法,不用去对抽象语法树每一种节点的属性做条件判断,就能遍历成序列的形式,可以说是:

c语言代码->pycparser的AST节点类->AST遍历序列

c语言代码->AST节点

这里展示不使用gcc或llvm编译器处理#include#define的方法,如果想处理这些语句可以看上一篇文章。

# 这里展示不用本地编译器的方法
# 但读取的文本序列,需去除#include #define这类语句才能生成AST
with open(filename, encoding='utf-8) as f:
	txt = f.read()
ast = c_parser.CParser().parse(txt)

AST节点->遍历序列

细心观察的朋友会注意到,在pycparser的c_ast.py文件内,对于节点的声明是这样的(以ArrayDecl节点为例):

class ArrayDecl(Node):
    __slots__ = ('type', 'dim', 'dim_quals', 'coord', '__weakref__')
    def __init__(self, type, dim, dim_quals, coord=None):
        self.type = type
        self.dim = dim
        self.dim_quals = dim_quals
        self.coord = coord

    def children(self):
        nodelist = []
        if self.type is not None: nodelist.append(("type", self.type))
        if self.dim is not None: nodelist.append(("dim", self.dim))
        return tuple(nodelist)

    def __iter__(self):
        if self.type is not None:
            yield self.type
        if self.dim is not None:
            yield self.dim

    attr_names = ('dim_quals', )

可以观察到,我们可以调用attr_names获取节点属性,调用节点的children()方法获取孩子节点。所以有了获取节点所有信息的思路:

# ast节点
node = ast
# ast节点名称
nodeName = node.__class__.__name__
# ast孩子节点
nodeChildren = node.children()
# ast节点属性
nodeAttributes = node.attr_names

缺点就是,children()会对孩子节点做筛查,如果不存在孩子节点,就不会返回对应的属性名称,要具体对每个节点类做分析还是不太友好。
据此,我们可以根据该操作写出所有的过程,代码如下:

from pycparser import parse_file, c_generator
from pycparser.plyparser import ParseError
from pycparser import c_parser
from pycparser.c_ast import * # ast节点类(所有节点类型都继承该文件中的Node类)
import os

def getAttribute(attr):
    print(attr)
    return ''


def getAst(node):
    nodeName = node.__class__.__name__
    nodeChildren = node.children()
    nodeAttributes = node.attr_names

    nodeAttr = [nodeName]
    for _, n in nodeChildren:
        nodeAttr.extend(getAst(n))

    for attr in nodeAttributes:
        attribute = getattr(node, attr) # 先获取属性
        nodeAttr.extend(getAttribute(attribute)) # 自定义当前节点属性str样式

    return nodeAttr

filePath = 'test3.c'

try:
    ast = None
    # ast = parse_file(filePath)
    with open(filePath) as f:
        txtList = f.readlines()
        txt = ''
        for each in txtList:
            if each.find('#include') != -1 :
                continue
            elif each.find('//') != -1:
                txt += each[:each.find('//')]
            else :
                txt += each
            txt += '\n'
    ast = c_parser.CParser().parse(txt)
    print(getAst(ast))
except ParseError as e:
    print('代码有错:'+str(e))
except Exception as r:
    print('错误:'+str(r))

到这里,就完成了所有ast节点的遍历操作,该代码还会对原有的#include和注释做删除。(没有删除#define)

如果要对每一个节点的具体属性做分析,可以看看上一篇文章,会有帮助。

你可能感兴趣的:(c语言,python,数据结构)