如何在 xtext DSL 中嵌入 C++ 代码?

How to embed C++ code in a xtext DSL?

在 Xtext/ANTLR 中处理 C++ 代码块的正确方法是什么?

我们正在为 DSL 编写一个基于 Xtext 的 eclipse 插件,它支持在明确定义的范围内(主要是 serial { /* ... */ } 块)添加 C++ 函数级代码,例如:

module m {
  chare c {
    entry void foo() {
      serial {
        // C++ code block
      }
    }
  }
}

有关更全面的示例,请参阅 here。然后将其移交给外部工具来处理进一步的 compilation/linking 步骤,因此我们不会从 eclipse 生成任何代码。

这里的问题是如何处理这些 C++ 代码块,特别是考虑到它们可能包含自己的大括号。这与 How to include Java Code Block in Xtext DSL? 非常相似,但现在我们满足于忽略该块(即没有内容辅助或语法突出显示并不理想但可以接受。)

在我们的 bison/flex-based 工具中,这是通过在解析器和词法分析器之间共享一个变量来完成的,该变量在某些语法规则内切换 "C++ parsing mode",使词法分析器 return 成为 CPROGRAM 标记除了相关的定界符(例如大括号)之外的所有内容。自然翻译似乎有一个自定义的 ANTLR 词法分析器,它使用语义谓词来达到相同的效果,例如

RULE_NON_BRACES: {in_braces}? ~('{' | '}')+;

作为第一个词法规则,但我找不到如何从 Xtext 语法访问该全局变量,因为在 bison 中似乎没有 "rule action" 的概念。还有其他非 "serial" 语法上下文,其中需要 C++ 代码,因此需要在解析器和词法分析器之间进行一些协调。

您的问题似乎更侧重于 DSL 词法分析器如何避免迷失在 C++ 代码中。基本答案是您需要 匹配 括号(例如,确保它们正确嵌套)。

我不知道你是如何定义 Xtext/ANTLR 词法规则来做到这一点的;我认为有一种丑陋的方式可以进入程序代码并开始阅读字符 one-by-one。这可能会有一些并发症;您的双亲匹配逻辑可能不得不担心 C++ 代码中的各种类型的引用。例如,

        {   //   this } isn't a match

        {   char x[]="} this isnt a match { either" }

其他 C++ 字符串引号可能会使这更难看清。对于像这样使用的 C++ 宏,您将如何处理?

        {
        #define rcb }
            {   rcb
        }

您可能必须制定一些关于如何在嵌入式 C++ 代码中处理 } 的特殊规则,并且您的 character-by-character 扫描必须了解此规则。

我认为你应该做的是在 C++ 中选择一个不太可能的字符序列作为终止符,而不是让它变得复杂,例如,

    ][[

我认为除了在字符串或注释中,或者

    }}}

然后简单地使用它。根本不需要匹配 parens。在几乎所有情况下,都不必触及要插入的 C++;在极少数情况下,它恰好包含该序列,一个微不足道的编辑(插入 space 或换行符)修复了它。现在你的词法分析器规则很简单,可以用你的标准词法分析器表达(我认为)。

如果你走这条路,我建议你选择一个相应的开头顺序来介绍C++代码,只是为了提醒reader需要一个有趣的顺序,例如

  serial {{{    <C++code>  }}}

  serial ]][   <C++code>   ][[

有了这个约定,即使是我丑陋的宏示例也很容易:

  serial {{{
      {
      #define rcb }
         {  rcb
      } 
  }}}

PS:这个有趣的符号技巧被称为 "domain (notation) escape"。这个问题出现在每个系统中(是的,在野外没有那么多,但我有一个 :) 允许一个人混合多个符号。 language/system 的顺序因口味而异。

如果您确实无法更改语法并且需要依赖匹配的花括号,那么您需要在 Java 中重新实现基于 flex 的解决方案(例如使用 jflex)并让 Xtext 使用该词法分析器。 我已在 blog post 中简要介绍了这一点。它还包含指向示例代码的指针,我在 Xtext 中使用了基于 jflex 的词法分析器。