如何在 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 的词法分析器。
在 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 的词法分析器。