【问题标题】:Automatically modify C++ Code: Transform code from a parse tree back to source code自动修改 C++ 代码:将代码从解析树转换回源代码
【发布时间】:2020-03-26 18:35:02
【问题描述】:

我正在尝试修改 C++ 代码。我得到一段代码和行号,我需要在给定的行号处应用代码。 像这样:

1 void foo(){
2 int a = 5;
3 int b = 10;
4 }

还有行号:2,3。现在我想在行号之后自动插入代码:

1 void foo(){
2 int a = 5;
3 newcode();
4 int b = 10;
5 newcode();
6 }

在另一个Thread 中,人们说 antlr 是解决这个问题的好方法。所以我尝试使用 antlr 运行时 api。生成解析树很容易。我还找到了修改它的方法。但是现在我不知道如何从解析树中取回源代码? 我真的不需要源代码,只需将解析树编译为可执行程序就足够了。我该怎么做?

是否有更简单的方法来解决我的问题?也许只是阅读代码,计算 \n 并在 2 和 3 \n 之后输入我的代码?

编辑: 对于我的学士论文,我得到了一段并行代码,我需要强制它执行给定的交错。因此,我需要编写一个工具来在代码中的给定行自动插入“EnterCriticalSection(...)”和“LeaveCriticalSection(...)”等指令。现在,我得到了另一份工作,重命名主函数并插入我自己的主函数。我认为这不适用于计算行数。

【问题讨论】:

  • 这听起来像XY problem。为什么需要以这种方式自动修改 C++ 代码?
  • 对于源到源的转换,您可能需要查看clang's libTooling
  • IMO 我很乐意回答所提出的问题 - 这是一个完全合理的问题 - 而不是建议提问者应该做其他事情。我也想知道怎么做。我认为它很可能无法完成,因为 ANTLR 是解析器(生成器)而不是转换器,所以如果它不能完成,那也很高兴知道。
  • BTW this answer by ANTLR 的作者将为您指明正确的方向(尽管它不是一本食谱)。
  • 如果你想在特定的行号修改 /insert 为什么你真的需要把它解析成树?只需使用一个好的旧 sed/awk 在行号处插入一段文本。

标签: c++ antlr transformation


【解决方案1】:

一种可能的解决方案是使用生成的解析树作为标记位置(每个TerminalNode 都有一个Token 实例,附加了它在原始源代码中的位置信息)。有了它,您可以开始从原始源流中复制未修改的文本,然后插入属于该位置的您自己的文本。之后复制下一个未修改的代码部分,然后插入您的下一个修改。循环执行此操作,直到到达 EOF。

此方案不关心最终格式,但我认为这可能不相关 - 您的任务听起来像是在为某些测量进行代码检测。

在给定两个解析树节点的情况下,这是我用来检索原始源代码的代码:

std::string MySQLRecognizerCommon::sourceTextForRange(tree::ParseTree *start, tree::ParseTree *stop, bool keepQuotes) {
  Token *startToken = antlrcpp::is<tree::TerminalNode *>(start) ? dynamic_cast<tree::TerminalNode *>(start)->getSymbol()
                                                                : dynamic_cast<ParserRuleContext *>(start)->start;
  Token *stopToken = antlrcpp::is<tree::TerminalNode *>(stop) ? dynamic_cast<tree::TerminalNode *>(start)->getSymbol()
                                                              : dynamic_cast<ParserRuleContext *>(stop)->stop;
  return sourceTextForRange(startToken, stopToken, keepQuotes);
}

//----------------------------------------------------------------------------------------------------------------------

std::string MySQLRecognizerCommon::sourceTextForRange(Token *start, Token *stop, bool keepQuotes) {
  CharStream *cs = start->getTokenSource()->getInputStream();
  size_t stopIndex = stop != nullptr ? stop->getStopIndex() : std::numeric_limits<size_t>::max();
  std::string result = cs->getText(misc::Interval(start->getStartIndex(), stopIndex));
  if (keepQuotes || result.size() < 2)
    return result;

  char quoteChar = result[0];
  if ((quoteChar == '"' || quoteChar == '`' || quoteChar == '\'') && quoteChar == result.back()) {
    if (quoteChar == '"' || quoteChar == '\'') {
      // Replace any double occurence of the quote char by a single one.
      replaceStringInplace(result, std::string(2, quoteChar), std::string(1, quoteChar));
    }

    return result.substr(1, result.size() - 2);
  }

  return result;
}

此代码取自MySQL Workbench parser module

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-17
    • 2010-09-17
    • 2013-02-24
    • 1970-01-01
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多