在C++中具有名称绑定的EDSL

EDSL with name binding in C++

本文关键字:绑定 EDSL 有名称 C++      更新时间:2023-10-16

是否可以用C++编写一个将值绑定到变量名的edsl?例如,我可以在Haskell中编写一个edsl,它允许我编写以下内容(另请参阅此问题):

prog3 :: StackProg Expr
prog3 = do
  push (IntL 3)
  push (IntL 4)
  a <- pop
  b <- pop
  return (Plus a b)

这产生了一个AST,其中ab是变量。类似的东西在C++中可能存在吗?我想要(按重要性排序)

  • 得到的edsl的可接受语法
  • 合理的AST
  • 对象(dsl)语言中的类型安全
  • 可以理解的机制

如果你想生成有效C++表达式的语法(可能是所有C++表达式的子集,就像do表示法将自己限制为monad操作一样),静态验证它们并使用它们,那么你最好的选择是Boost.Proto。为了简洁地描述它,它本身就是一个编写和描述EDSL的EDSL。

我不会再详细介绍如何使用它了。虽然它可能很难学会使用,尤其是如果你不习惯C++元编程,文档很好,如果你曾经写过语法,我相信你会找到自己的分数。在我的另一个答案中,我向某人介绍了如何编写EDSL,该语法只接受简单的算术表达式,并消耗它们来计算它们的导数,所以你可能想检查一下。

至于你的确切问题,恐怕答案要么是简短的"不,你不能那样做",要么是冗长的"你可以在一定程度上像Boost.Fenix所展示的那样做,但考虑到EDSL用户的神秘错误和/或额外的编译时间,你可能不值得花时间来实现它"。我对此的推理是,您想要做的事情符合两个层面:do表示法是Haskell特定的功能,同时使用语法树并在EDSL本身的层面上为其提供语义。

碰巧的是,典型的Proto风格EDSL是有效的C++表达式,并且该语言不提供该级别的作用域,变量在单独的语句中声明。例如,_a + _b是一个有效的C++片段,因为_a_b是Phoenix提供的C++变量,但不是EDSL中的有效程序,因为_a_b未绑定。是的,错误会被发现,但你必须自己实现。相比之下,do表示法是Haskell的一部分,因此任何EDSL都可以免费继承它。也就是说,return (a + b)本身从来都不是有效的——需要有一些a和一些b

不过有一些事情需要记住。C++11提供lambda表达式,因此您实际上可以在这里获得一些范围——但这些表达式在EDSL中是不透明的,语法树将只显示一个变量。一些内省可能会发现,该变量对某些类型是可调用的,但仅此而已。即使你要求lambdas在EDSL中返回一个值,也无法告诉他们还能做什么。这并不总是值得担心的,我认为这非常适合某些EDSL。

类似地,C++11使EDSL表达式的部分"因子化"变得更加容易。这与do表示法的a <- foo糖不太等价,但它与let a = foo等价。因此,事实上,你可以毫不费力地做出以下"正确的事情":

auto double_pop = make_tuple(pop(), pop());
auto program = (push(3), push(4), consume(double_pop));

这可能相当于以下的单一和人为的:

program = do
  let a = pop
  return consume `ap` a `ap` a

(由于Boost.Proto最初是一个C++03库,在使用C++11的auto之前,请确保仔细阅读文档,IIRC有一个警告。)

不,不能用C++直接建模。然而,在C++中,您可以嵌入其他引擎,例如Lua,它通常用作扩展引擎,适用于DSL。

请参阅StackOVerflow中的答案。

在C++中嵌入Lua的步骤:

  1. 这是Lua的网页
  2. 请在此处下载源代码
  3. 将Lua集成到您的C++应用程序中