3

红宝石爱好者!我正在尝试用 ruby​​ 编写 DSL,我希望能够创建一些魔术方法(不确定这是我想要的最准确的术语)。

我希望能够执行以下操作:

a = [1, 2, 3]
b = 2

(a contains b)

并让它解析为真或假。

本质上,我如何定义函数“包含”,以便它接受一个数组a和一个变量b并执行a.contains?(b),但没有所有相关的 ruby​​ 特定语法?

4

3 回答 3

2

如果你想要一个不使用 ruby​​ 语法的 DSL,你至少需要编写一个解析器来执行转换(raganwalds rewrite lib 可能是一个起点,http ://github.com/raganwald/rewrite )

也就是说,你不想这样做。这是需要维护的更多代码,Ruby 已经做出了很多艰难的决定,使得编写语言语法变得困难。自然语言编程对于非程序员来说也不是很容易使用,因为格式的准确性是具有挑战性的方面(例如,参见 applescript)。

于 2010-06-20T01:51:39.090 回答
2

你可以滥用method_missing。棘手的是,您不能直接访问块局部变量。您必须在某处捕获块内部绑定(不幸block.binding的是返回块的外部绑定)。

您可以运行以下代码:

DSL.new do
  a = [1, 2, 3]
  b = 2
  a contains b
end

具有以下内容:

class DSL
  attr_reader :last_binding

  def initialize(&block)
    set_trace_func method(:trace).to_proc
    instance_eval(&block)
    set_trace_func nil
  end

  def trace(event, file, line, id, binding, klass)
    if event.to_s == "call" and klass == self.class and id.to_s == "method_missing"
      @last_binding ||= @current_binding
      set_trace_func nil
    else
      @current_binding = binding
    end
  end

  def lvars
    eval('local_variables', last_binding).map(&:to_s)
  end

  def method_missing(name, *args)
    name = name.to_s
    if lvars.include? name
      eval(name, last_binding).send(*args.flatten)
    else
      ["#{name}?", *args]
    end
  end
end

class Array
  alias contains? include?
end
于 2010-06-20T12:07:16.760 回答
0

我能想到的最接近的事情是:

def contains var, useless_symbol, arr
  arr.include? var
end

然后你可以这样称呼它:

contains b, :in, a


我认为没有任何方法可以在您自己的函数中使用中缀表示法。

于 2010-06-20T01:18:42.843 回答