翻译DSL的块和语句

时间:2009-05-09 12:03:51

标签: ruby dsl

我想编写一个简单的Ruby DSL来将一些语句和表达式翻译成另一种语言。一个基本的例子是:

some_function {
    t + 2
}

这里,t不是ruby变量,因此块不能(并且不能!)由Ruby评估。所以我最好的选择是使用解析输出(或AST)自己进行翻译。为此,我可以使用ParseTree和ruby2ruby。但是,我还有其他想要使用的构造,例如:

1.upto(10) {|x|
    some_function {
        t + x
    }
}

在这里,我有一个局部变量,我需要得到它的值才能进行翻译。但是,在执行块评估的函数中,我无法访问调用块的局部变量。如果它是一个全局变量($ x),我可以检查它的名字是否存在于global_variables数组中,并且在最坏的情况下使用eval,但是如果可能的话,我怎么能这样做呢?

更新

只是为了清理事情。就像我原来说的那样,我正在使用ruby2ruby(以及ParseTree)来获取与块对应的AST(使用to_sexp)。但是当我在块中使用局部变量时,我会遇到以下情况:

[:dvar, :x]

因此,我需要从其名称中获取变量的值作为字符串/符号。我不能使用method_missing或instance_eval,因为我想将整个表达式翻译成另一种语言或语法(如RPN)。

另一种不基于ParseTree的解决方案将受到欢迎,因为它显然不完全支持Ruby 1.9。

3 个答案:

答案 0 :(得分:1)

要获取变量值,请使用proc的绑定:

def some_function(&block)
  b = block.binding
  p [b.eval("t"), b.eval("x")]
end

t = 1
1.upto(10) {|x|
  some_function {
    t + x
  }
}

答案 1 :(得分:0)

  

这里,t不是ruby变量   因此块不能(而且一定不能!)   由Ruby评估。

“未评估”限制的任何原因?似乎method_missing可以优雅地处理评估“缺失”t变量,但Ruby会自动取消引用x变量。

答案 2 :(得分:0)

您可以对具有t的对象使用instance_eval。

class Context
  attr_accessor :t

  def initialize(_t)
    @t = _t
  end
end

def some_function(&block)
  puts Context.new(1).instance_eval(&block)
end

1.upto(10) {|x|
  some_function {
    t + x
  }
}