【问题标题】:Ruby: How to process a "block" of code (domain specific language)Ruby:如何处理代码“块”(领域特定语言)
【发布时间】:2012-07-11 06:22:11
【问题描述】:

基本上,我想象一些看起来像这样的伪代码

build_command_list {
   add_command("cmd 1")
   add_command("cmd 2")
   conditional_branch( some_cond ) {
      add_command("cmd3")
      add_command("cmd4")
   }
   conditional_branch( some_other_cond ) {
      add_command("cmd 5")
   }
}

build_command_list 方法将获取一个代码块,然后递归地构建一个树结构,其中每个节点都是一个命令,一些解释器将获取该树并从根执行它,这是该块中的第一个命令代码(在这种情况下,"cmd 1"

add_commandconditional_branch 是确定如何创建节点的其他方法。

有像conditional branch 这样的命令告诉解释器评估一个条件,如果为真,它将处理它的命令列表。

我想知道 ruby​​ 是否有一些东西可以让我像上面的示例一样编写一段代码并获取代码并解析它?

如果没有,也许有人可以为我建议一种方法来编写输入,以便我可以从一组命令构建正确的命令树?

【问题讨论】:

    标签: ruby


    【解决方案1】:

    你可以写一个 DSL 来做这个:

    class CommandList
      def add_command(str)
        puts "Adding command #{str}"
      end
    
      def conditional_branch(condition, &block)
        b = ConditionalBranch.new(condition)
    
        puts "With condition: #{condition} {"
        b.instance_eval(&block)
        puts "}"
      end
    end
    
    class ConditionalBranch < CommandList
      def initialize condition
        @condition = condition
      end
    end
    
    def build_command_list &block
      c = CommandList.new
      c.instance_eval(&block)
    end
    

    DSL 使用

    build_command_list {
       add_command("cmd 1")
       add_command("cmd 2")
       conditional_branch( "some_cond" ) {
          add_command("cmd3")
          add_command("cmd4")
       }
       conditional_branch( "some_other_cond" ) {
          add_command("cmd 5")
       }
    }
    

    输出

    Adding command cmd 1
    Adding command cmd 2
    With condition: some_cond {
    Adding command cmd3
    Adding command cmd4
    }
    With condition: some_other_cond {
    Adding command cmd 5
    }
    

    请注意,这可能不会按照您希望的方式处理条件,这只是您如何使用 instance_eval 在给定对象的上下文中评估块的示例

    【讨论】:

    • Ruby 非常强大。好吧,我猜是DSL。不知道你可以创造这样的东西哈哈。对于这种情况应该没问题,因为我真的只需要一种方法来识别它是一个包含块的节点,以便我可以执行适当的递归调用来解决这个问题。
    【解决方案2】:

    您在这里所说的称为 DSL(领域特定语言)。

    您可以在 ruby​​ 中实现内部和外部 DSL。

    对于外部 DSL,您需要创建一个解析器。这就是为什么使用 ruby​​ 创建内部 DSL 通常更好的原因。

    我写了一篇关于可用于实现此目的的基本技术的博文:http://blog.nofail.de/2010/02/writing-your-own-dsl-with-ruby/

    【讨论】:

    • 要点听起来像是我想要实现的。我提供了一组方法,允许其他用户与解释器交互,他们将使用一组方法调用来指示将创建哪些命令,以及树的结构应该如何,用普通的 ruby​​。我第一次尝试使用代码块是显式添加命令,告诉解释器以下是块的开始和结束,但它很难看而且很容易搞砸(我的意思是,它有效,但是这真的很不友好)。我会看看内部 DSL 是否能解决问题。
    猜你喜欢
    • 2019-01-05
    • 1970-01-01
    • 2010-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多