traduzindo blocos e declarações para uma DSL
Pergunta
Eu quero escrever um simples rubi DSL para traduzir algumas afirmações e expressões em outro idioma. Um exemplo básico seria:
some_function {
t + 2
}
Aqui, t não é uma variável rubi e, portanto, o bloco não pode (e não deve!) Ser avaliada por Ruby. Assim, a minha melhor aposta seria a de usar a saída de análise (ou AST) para fazer a tradução me. Para fazê-lo, eu posso usar ParseTree e ruby2ruby. No entanto, tenho outras construções que eu gostaria de usar, por exemplo:
1.upto(10) {|x|
some_function {
t + x
}
}
Aqui, eu tenho uma variável local e eu preciso para obter o seu valor, a fim de fazer a minha tradução. No entanto, na função que faz a avaliação do bloqueio, eu não tenho acesso às variáveis ??locais do bloco de chamada. Se fosse uma variável global ($ x), eu poderia verificar se o seu nome existe na matriz global_variables e, no pior uso eval caso, mas como eu poderia fazê-lo para uma variável local, se possível em tudo?
Update:
Apenas para esclarecer as coisas. Como eu disse inicialmente, estou usando ruby2ruby (e, portanto, ParseTree) para obter a AST (usando to_sexp) correspondente ao bloco. Mas ao usar uma variável local dentro do meu bloco, me deparo com o seguinte:
[:dvar, :x]
E assim, eu precisaria para obter o valor de uma variável de seu nome como uma string / símbolo. E eu não posso usar method_missing ou instance_eval, porque eu quero traduzir a expressão inteira para outro idioma ou sintaxe (como um RPN).
Outra solução não baseada em ParseTree seria bem-vinda, no entanto, uma vez que, aparentemente, não é totalmente compatível com o Ruby 1.9.
Solução
Para obter os valores das variáveis, utilize o proc de ligação:
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
}
}
Outras dicas
Aqui, t não é uma variável rubi e portanto, o bloco não pode (e não deve!) ser avaliado por Ruby.
Você tem algum motivo para a restrição "não avaliada"? Parece que method_missing seria elegantemente lidar com a avaliação da "falta" variável t
, mas o ruby ??seria desreferenciava automaticamente a variável x
.
Você pode usar instance_eval contra um objeto com t
.
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
}
}