2009-05-09 8 views
4

Я хочу написать простой Ruby DSL для перевода некоторых операторов и выражений на другой язык. Основной пример будет:перевод блоков и операторов для DSL

some_function { 
    t + 2 
} 

Здесь т не является переменным рубином и, таким образом, блок не может (и не должен!) Быть оценен Ruby. Поэтому лучше всего использовать вывод синтаксического анализа (или AST) для самостоятельного перевода. Для этого я могу использовать ParseTree и ruby2ruby. Тем не менее, у меня есть другие конструкции, которые я хотел бы использовать, например:

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

Вот, у меня есть локальная переменная, и мне нужно, чтобы получить его значение для того, чтобы сделать свой перевод. Однако в функции, которая выполняет оценку блока, у меня нет доступа к локальным переменным вызывающего блока. Если бы это была глобальная переменная ($ x), я мог бы проверить, существует ли ее имя в массиве global_variables и в худшем случае используется eval, но как я могу сделать это для локальной переменной, если возможно вообще?

Update:

Просто чтобы прояснить вещи. Как я уже сказал, я использую ruby2ruby (и, следовательно, ParseTree), чтобы получить AST (используя to_sexp), соответствующий блоку. Но при использовании локальной переменной внутри моего блока, я сталкиваюсь следующее:

[:dvar, :x] 

И таким образом, я должен был бы получить значение переменной от ее имени в качестве строки/символ. И я не могу использовать method_missing или instance_eval, потому что я хочу перевести все выражение на другой язык или синтаксис (например, RPN).

Другое решение, не основанное на ParseTree, будет приветствоваться, тем не менее, поскольку оно, очевидно, не полностью поддерживается Ruby 1.9.

+0

Кстати, ваш проект с открытым исходным кодом? Я работаю над DSL, которому в какой-то момент может понадобиться другой бэкэнд, поэтому эта техника мне интересна. –

ответ

1

Чтобы получить значения переменных, использовать связывание прок в:

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

OP также хочет несвязанные переменные, такие как 't' в исходном примере, поэтому вам придется поймать исключения NameError. – flicken

+0

Так как я пересекаю AST для обработки опций и переменных, я могу отфильтровать из eval() переменные, которые не связаны на рубине, но это было бы объявлено ранее DSL. –

0

Здесь т не является переменным рубином и таким образом, блок не может (и не должен!) быть оценен Ruby.

Любые причины для «не оцененных» ограничений? Кажется, что method_missing будет элегантно обрабатывать оценку «отсутствующей» переменной t, но Ruby автоматически разыгрывает переменную x.

+0

Для такого простого примера, method_missing может поместиться, но мне нужно, чтобы перевести все выражение для некоторых более сложных случаев, как: some_function { , если т <12 - еще т конца } И там, Мне нужно также перевести инструкцию if. –

+0

Делает смысл. Вместо этого используйте привязки Kernel # для разыменования любой переменной. Я напишу еще один ответ с подробностями. – flicken

+0

Nevermind, Justin Love избил меня. – flicken

0

Вы можете использовать instance_eval для объекта с 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 
    } 
} 
+0

Подобно method_missing, это было бы решением, если бы я хотел, чтобы Ruby оценил выражение, но я действительно хочу перевести выражение, чтобы другой инструмент оценил его. –

 Смежные вопросы

  • Нет связанных вопросов^_^