【问题标题】:How to get only function blocks using sly如何使用 sly 仅获取功能块
【发布时间】:2019-07-25 19:27:28
【问题描述】:

我需要获取函数块(定义和所有内容,而不仅仅是声明),以便获取函数依赖图。从函数依赖图中,识别连接的组件并将我疯狂的巨大 C 代码库模块化,一次一个文件。

问题:我需要一个 C 解析器来识别功能块,仅此而已。我们有自定义类型等,但签名去

storage_class return_type function_name ( comma separated type value pairs )
{

//some content I view as generic stuff

}

我想出的解决方案:使用 sly 和 pycparser 显然,就像任何理智的人都会做的那样。

pycparser 的问题:需要从其他文件编译预处理器,只是为了识别代码块。就我而言,事情达到了 6 个级别的深度。很抱歉,我无法显示实际代码。

尝试使用 Sly 编写代码:

from sly import Lexer, Parser
import re

def comment_remover(text):
    def replacer(match):
        s = match.group(0)
        if s.startswith('/'):
            return " " # note: a space and not an empty string
        else:
            return s
    pattern = re.compile(
        r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
        re.DOTALL | re.MULTILINE
    )
    return re.sub(pattern, replacer, text)

class CLexer(Lexer):
    ignore = ' \t\n'
    tokens = {LEXEME, PREPROP, FUNC_DECL,FUNC_DEF,LBRACE,RBRACE, SYMBOL}
    literals = {'(', ')',',','\n','<','>','-',';','&','*','=','!'}
    LBRACE = r'\{'
    RBRACE = r'\}'
    FUNC_DECL = r'[a-z]+[ \n\t]+[a-zA-Z_0-9]+[ \n\t]+[a-zA-Z_0-9]+[ \n\t]*\([a-zA-Z_\* \,\t\n]+\)[ ]*\;'
    FUNC_DEF = r'[a-zA-Z_0-9]+[ \n\t]+[a-zA-Z_0-9]+[ \n\t]*\([a-zA-Z_\* \,\t\n]+\)'
    PREPROP = r'#[a-zA-Z_][a-zA-Z0-9_\" .\<\>\/\(\)\-\+]*'
    LEXEME = r'[a-zA-Z0-9]+'
    SYMBOL = r'[-!$%^&*\(\)_+|~=`\[\]\:\"\;\'\<\>\?\,\.\/]'


    def __init__(self):
        self.nesting_level = 0
        self.lineno = 0

    @_(r'\n+')
    def newline(self, t):
        self.lineno += t.value.count('\n')

    @_(r'[-!$%^&*\(\)_+|~=`\[\]\:\"\;\'\<\>\?\,\.\/]')
    def symbol(self,t):
        t.type = 'symbol'
        return t

    def error(self, t):
        print("Illegal character '%s'" % t.value[0])
        self.index += 1

class CParser(Parser):
    # Get the token list from the lexer (required)
    tokens = CLexer.tokens

    @_('PREPROP')
    def expr(self,p):
        return p.PREPROP

    @_('FUNC_DECL')
    def expr(self,p):
        return p.FUNC_DECL

    @_('func')
    def expr(self,p):
        return p.func

    # Grammar rules and actions
    @_('FUNC_DEF LBRACE stmt RBRACE')
    def func(self, p):
        return p.func_def + p.lbrace + p.stmt + p.rbrace

    @_('LEXEME stmt')
    def stmt(self, p):
        return p.LEXEME

    @_('SYMBOL stmt')
    def stmt(self, p):
        return p.SYMBOL

    @_('empty')
    def stmt(self, p):
        return p.empty

    @_('')
    def empty(self, p):
        pass

with open('inputfile.c') as f:
    data = "".join(f.readlines())
    data = comment_remover(data)
    lexer = CLexer()
    parser = CParser()
    while True:
        try:
            result = parser.parse(lexer.tokenize(data))
            print(result)
        except EOFError:
            break

错误:

None
None
None
.
.
.
.
None
None
yacc: Syntax error at line 1, token=PREPROP
yacc: Syntax error at line 1, token=LBRACE
yacc: Syntax error at line 1, token=PREPROP
yacc: Syntax error at line 1, token=LBRACE
yacc: Syntax error at line 1, token=PREPROP
.
.
.
.
.

输入:

#include <mycustomheader1.h> //defines type T1
#include <somedir/mycustomheader2.h> //defines type T2
#include <someotherdir/somefile.c>

MACRO_THINGY_DEFINED_IN_SOMEFILE(M1,M2) 

static T1 function_name_thats_way_too_long_than_usual(int *a, float* b, T2* c)
{

 //some code I don't even care about at this point

}

extern T2 function_name_thats_way_too_long_than_usual(int *a, char* b, T1* c)
{

 //some code I don't even care about at this point

}

期望的输出:


function1 : 

static T1 function_name_thats_way_too_long_than_usual(int *a, float* b, T2* c)
{

 //some code I don't even care about at this point

}

function2 :

extern T2 function_name_thats_way_too_long_than_usual(int *a, char* b, T1* c)
{

 //some code I don't even care about at this point

}


【问题讨论】:

  • 如果您的问题是预处理,您是否考虑过在解析之前将输入传递给预处理器?
  • 不,我认为这不是重点。 C 中的函数有一个模式。我需要一个下推自动机来识别序列。 gcc -E 可能会加快符号部分的识别速度,但我仍然需要一个 PDA 来识别功能块。对吗?

标签: python c python-3.x pycparser


【解决方案1】:

pycparser 有一个 func_defs 示例可以完全满足您的需求,但是 IIUC 您在预处理方面遇到问题?

This post 详细描述了为什么 pycparser 需要预处理文件,以及如何设置它。如果您控制构建系统,它实际上非常容易。预处理文件后,上面提到的示例应该可以工作。

我还要注意,由于函数指针的存在,静态查找函数依赖关系并不是一个简单的问题。您也无法使用单个文件准确地执行此操作 - 这需要多文件分析。

【讨论】:

  • 是否也只显示函数定义或函数代码块。第一个我可以用正则表达式实现,如问题所示。后者是我需要的,返回函数 def 和函数体。
  • @LazyCoder:它显示了函数定义的位置并为您提供了它们的 AST 节点(这方式比使用正则表达式更强大)。拥有节点后,您可以使用 C 生成器 (github.com/eliben/pycparser/blob/master/examples/c-to-c.py) 将它们发送回 C 并获取您的定义。但我的印象是您想分析这些函数的 AST 以查找对其他函数的调用,而不是将正文打印出来
  • 是的,我需要获取函数的 AST 并获取对其他函数的函数调用。函数指针案例在我的代码中很少见。结合 cmets,我需要先预处理,然后使用 pycparser 给我 AST,然后从 AST 中我需要找到对其他函数的调用?有没有更简单的方法?
  • @LazyCoder:pycparser 将为您提供每个函数定义的 AST。您可以分析此 AST 以查找调用。在examples 目录中还有另一个示例,它可以找到所有调用,因此一旦您拥有预处理代码,您应该能够相当容易地将这些示例组合成一个工作解决方案。
  • 圣船在港口!你是pycparser的作者吗?对不起,你的工作很辛苦,伙计。我只是不想向麻雀扔大炮。我会看看我能用你的代码做什么。
猜你喜欢
  • 2020-03-22
  • 1970-01-01
  • 2015-06-01
  • 1970-01-01
  • 2017-03-01
  • 2019-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多