Pregunta

Quiero escribir un DSL Ruby simple para traducir algunas declaraciones y expresiones a otro idioma.Un ejemplo básico sería:

some_function {
    t + 2
}

Aquí, t no es una variable de Ruby y, por lo tanto, Ruby no puede (¡y no debe!) evaluar el bloque.Entonces, mi mejor opción sería usar el resultado del análisis (o AST) para hacer la traducción yo mismo.Para hacerlo, puedo usar ParseTree y ruby2ruby.Sin embargo, tengo otras construcciones que me gustaría utilizar, por ejemplo:

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

Aquí tengo una variable local y necesito obtener su valor para poder realizar mi traducción.Sin embargo, en la función que realiza la evaluación del bloque, no tengo acceso a las variables locales del bloque que llama.Si fuera una variable global ($x), podría verificar si su nombre existe en la matriz global_variables y en el peor de los casos usar eval, pero ¿cómo podría hacerlo para una variable local, si es posible?

Actualizar:

Sólo para aclarar las cosas.Como dije originalmente, estoy usando ruby2ruby (y por lo tanto ParseTree) para obtener el AST (usando to_sexp) correspondiente al bloque.Pero cuando uso una variable local dentro de mi bloque, encuentro lo siguiente:

[:dvar, :x]

Y por lo tanto, necesitaría obtener el valor de una variable a partir de su nombre como una cadena/símbolo.Y no puedo usar método_missing o instancia_eval, porque quiero traducir la expresión completa a otro idioma o sintaxis (como un RPN).

De todos modos, otra solución que no esté basada en ParseTree sería bienvenida, ya que aparentemente no es totalmente compatible con Ruby 1.9.

¿Fue útil?

Solución

Para obtener los valores de las variables, use el enlace del proceso:

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
  }
}

Otros consejos

Aquí, T no es una variable Ruby y, por lo tanto, el bloque no puede (¡no debe!) Ruby.

¿Alguna razón para la restricción "no evaluado"?Parece que método_faltante manejaría elegantemente la evaluación de los "faltantes" t variable, pero Ruby automáticamente desreferenciaría la x variable.

Puede usar instancia_eval contra un objeto con 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
  }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top