如何清除包含C函数声明的字符串中的注释和中间空白



在我用C++编写的程序中,我需要获取一组字符串,每个字符串都包含一个C函数的声明,并对它们执行一些操作。

其中一个操作是比较一个函数是否等于另一个函数。为了做到这一点,我计划只修剪掉对函数语义没有影响的注释和中间空白,然后进行字符串比较。但是,我希望在字符串中保留空白,因为删除空白会更改函数产生的输出。

我可以写一些代码,在字符串上迭代并输入";字符串模式";每当遇到引号("(并识别转义引号时,但我想知道是否有更好的方法可以做到这一点。一个想法是使用一个完整的C解析器,在函数字符串上运行它,忽略所有注释和多余的空白,然后再次将AST转换回字符串。但环顾一些C语法分析器,我觉得大多数语法分析器都很难与我的源代码集成(如果我错了,就证明我错了(。也许我可以尝试使用yacc或其他东西,使用现有的C语法,并自己实现解析器。。。

那么,有什么最好的方法吗?

编辑:

我正在编写的程序采用了一个抽象模型,并将其转换为C代码。该模型由一个图组成,其中节点可能包含也可能不包含C代码段(更准确地说,是一个C函数定义,其中它的执行必须是完全确定的(即不允许全局状态(,并且不允许内存操作(。该程序在图上进行模式匹配,并合并和拆分遵守这些模式的某些节点。然而,只有当节点表现出相同的功能(即,如果它们的C函数定义相同(时,才能执行这些操作。这个";检查它们是否相同";将通过简单地比较包含C函数声明的字符串来完成。如果它们一个字符接一个字符地相同,那么它们就是相等的。

由于模型生成的性质,这是一种非常合理的比较方法,前提是删除了注释和多余的空白,因为这是唯一可能不同的因素。这就是我面临的问题——如何用最少的实现工作量做到这一点?

比较一个函数是否等于另一个函数是什么意思?有了适当精确的含义,这个问题就被认为是不可判定的!

你没有告诉我你的程序到底在做什么。正确解析所有真正的C程序并非易事(因为C语言的语法和语义并没有那么简单!(。

您是否考虑过使用现有的工具或库来帮助您?LLVM Clang是一种可能性,或者通过插件扩展GCC,或者用MELT编码的扩展更好。

但是,如果不了解您的真正目标,我们就无法为您提供更多帮助。解析C代码可能比您想象的更复杂。

看起来你可以通过简单的岛语法来去除注释、字符串文字和折叠空格(制表符,'\n'(。由于我使用的是AXE,我写了一个快速语法给你。您可以使用Boost编写一组类似的规则。精神

#include <axe.h>
#include <string>
template<class I>
std::string clean_text(I i1, I i2)
{
    // rules for non-recursive comments, and no line continuation
    auto endl = axe::r_lit('n');
    auto c_comment = "/*" & axe::r_find(axe::r_lit("*/"));
    auto cpp_comment = "//" & axe::r_find(endl);
    auto comment = c_comment | cpp_comment;
    // rules for string literals
    auto esc_backslash = axe::r_lit("\\");
    auto esc_quote = axe::r_lit("\"");
    auto string_literal = '"' & *(*(axe::r_any() - esc_backslash - esc_quote) 
        & *(esc_backslash | esc_quote)) & '"';
    auto space = axe::r_any(" tn");
    auto dont_care = *(axe::r_any() - comment - string_literal - space);
    std::string result;
    // semantic actions
    // append everything matched
    auto append_all = axe::e_ref([&](I i1, I i2) { if(i1 != i2) result += std::string(i1, i2); });
    // append a single space
    auto append_space = axe::e_ref([&](I i1, I i2) { if(i1 != i2) result += ' '; });
    // island grammar for text
    auto text = *(dont_care >> append_all 
        & *comment
        & *string_literal >> append_all
        & *(space % comment) >> append_space)
        & axe::r_end();
    if(text(i1, i2).matched)
        return result;
    else
        throw "error";
}

所以现在你可以进行文本清理:

std::string text; // this is your function
text = clean_text(text.begin(), text.end());

您可能还需要为多余的";"创建规则,空块{}等。您可能还需要合并字符串文字。需要走多远取决于函数的生成方式,最终可能会编写相当大一部分C语法。

AXE库即将在boost许可证下发布
我没有测试代码。

也许您想要解析的C函数并不像我们猜测的那样通用(以其文本形式,也由真正的编译器解析(。

您可能会考虑采取其他方式:

定义一个小型领域特定语言(它的语法可能比C简单得多(,而不是解析C代码,而是用另一种方式:用户将使用DSL,您的工具将从DSL生成C代码,稍后由您常用的C编译器编译(。

您的DSL实际上可以是的描述,您的抽象模型与更多转换为C函数的过程部分混合在一起。由于您关心的C函数非常具体,因此生成它们的DSL可能很小。

(想象一下,许多像ANTLR、YACC或Bison这样的解析器生成器都是基于类似的想法构建的(。

事实上,我在MELT中做了一些非常类似的事情(特别是读了我的DSL2011论文(。您可能会发现一些关于设计转换为C.的DSL的有用技巧

最新更新