在C ++中使用名称绑定的EDSL

时间:2013-02-21 08:43:11

标签: c++ haskell dsl

是否可以在C ++中编写一个将值绑定到变量名称的edsl?例如,我可以在Haskell中编写一个edsl,允许我编写以下(see also this question)

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)语言中的类型安全性
  • 可理解的机制。

2 个答案:

答案 0 :(得分:2)

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

Check out this answer, also in StackOVerflow

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

  1. This is the webpage for Lua
  2. Download the source here
  3. Integrate Lua in your C++ application

答案 1 :(得分:2)

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

我不会详细介绍如何使用它。虽然可能很难学会使用,特别是如果你不习惯C ++元编程,文档很棒,如果你曾经写过语法我相信你会找到你的分数。在Boost.Proto中,我向某人介绍了如何使用只接受简单算术表达式的语法来编写EDSL,并使用它们来计算它们的导数,因此您可能需要检查它。

至于你的确切问题,我担心答案必须是短暂的“不,你不能那样做”,或者说“你可以做到这一点,就像Boost.Phoenix所展示的那样,但是考虑到您的EDSL用户的神秘错误和/或额外的编译时间,它可能不值得您花时间实现它“。我的理由是你想做的事情适合两个层面:do-notation是一个Haskell特定的功能,同时消耗语法树并在EDSL本身的层面上给它语义。

正如它所发生的那样,典型的Proto风格的EDSL是有效的C ++表达式,并且该语言不提供该级别的范围,变量在单独的语句中声明。例如,_a + _b是一个有效的C ++片段,因为_a_b是由Phoenix提供的C ++变量,但不是EDSL中的有效程序,因为_a和{{ 1}}未绑定。是的,错误将被捕获,但您必须自己实现。相比之下,do-notation是Haskell的一部分,因此任何EDSL都可以免费继承它。也就是说,_b本身永远无效 - 需要有一些return (a + b)和一些a

但有些事情要记住。 C ++ 11提供了lambda表达式,所以你实际上可以在这里得到一些范围 - 但是在EDSL中它们是不透明的,语法树只会显示一个变量。一些内省可能会发现该变量对于某些类型是可调用的,但就是这样。即使你要求lambda在EDSL中返回一个值,也没有告诉他们可以做什么。这并不总是值得担心,我会说它非常适合某些EDSL。

类似地,C ++ 11使得“分解”EDSL表达式的部分变得更加容易。这并不等同于符号的b糖,但它相当于a <- foo。所以你可以毫无困难地让下面的事情做'正确的事':

let a = foo

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

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

(因为Boost.Proto最初是作为C ++ 03库开始的,所以在使用C ++ 11 program = do let a = pop return consume `ap` a `ap` a 之前一定要仔细检查文档,IIRC有一个警告。)