0

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

其中一种操作是比较一个函数是否等于另一个函数。为此,我计划只修剪掉对函数语义没有影响的注释和中间空格,然后进行字符串比较。但是,我想在字符串中保留空格,因为删除它会改变函数产生的输出。

我可以编写一些代码迭代字符串字符并在"遇到引号()时进入“字符串模式”并识别转义引号,但我想知道是否有更好的方法来做到这一点。一个想法是使用成熟的 C 解析器,在函数字符串上运行它,忽略所有注释和过多的空格,然后再次将 AST 转换回字符串。但是环顾一些 C 解析器,我觉得大多数与我的源代码集成都是一个婊子(如果我是的话,证明我错了)。也许我可以尝试使用yacc或使用现有的 C 语法并自己实现解析器......

那么,关于最好的方法的任何想法?

编辑:

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

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

4

3 回答 3

4

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

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

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

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

于 2011-11-06T10:27:43.980 回答
1

看起来您可以使用简单的岛语法来删除注释、字符串文字和折叠空格(制表符,'\n')。由于我正在使用 AX ,因此我为您编写了一个快速语法。您可以使用 Boost.Spirit 编写一组类似的规则。

#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(" \t\n");
    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 语法。

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

于 2011-11-09T01:21:45.357 回答
0

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

您可能会考虑以相反的方式做事:

定义一种小型领域特定语言(它的语法可能比 C 更容易解析)而不是解析 C 代码,而是以另一种方式进行:用户将使用您的 DSL,而您的工具将生成 C来自 DSL的代码(稍后由常用的 C 编译器编译)。

您的 DSL 实际上可能是您的抽象模型的描述,其中包含更多转换为 C 函数的程序部分。由于您关心的 C 函数非常具体,因此生成它们的 DSL 可能很小。

(认为​​许多解析器生成器,如 ANTLR 或 YACC 或 Bison 都是基于类似的想法构建的)。

实际上,我在MELT中做了一些非常相似的事情(尤其是我的DSL2011 论文)。您可能会发现一些关于设计翻译成 C 的 DSL 的有用技巧。

于 2011-11-06T19:53:37.823 回答