Что означает ключевое слово send в AST в Ruby?
Я пытаюсь изучить лексер и анализатор Ruby (анализатор whitequark), чтобы узнать больше о процедуре дальнейшей генерации машинного кода из сценария Ruby.
При разборе следующей строки кода Ruby.
def add(a, b)
return a + b
end
puts add 1, 2
Это приводит к следующему обозначению S-выражения.
s(:begin,
s(:def, :add,
s(:args,
s(:arg, :a),
s(:arg, :b)),
s(:return,
s(:send,
s(:lvar, :a), :+,
s(:lvar, :b)))),
s(:send, nil, :puts,
s(:send, nil, :add,
s(:int, 1),
s(:int, 3))))
Может ли кто-нибудь объяснить мне определение ключевого слова :send в результирующей нотации S-выражения?
2 ответа
Ruby построен на основе парадигмы "все является объектом". Тем не менее, все, включая цифры, является объектом.
Операторы, как мы видим их в простом рубиновом коде, являются не чем иным, как синтаксическим сахаром для вызовов методов соответствующего объекта:
3.14.+(42)
#⇒ 45.14
Вышеизложенное именно так относится к Ruby 3.14 + 42
краткая запись. Это, в свою очередь, может быть написано с использованием универсального Object#send
:
3.14.send :+, 42
#⇒ 45.14
Последнее следует читать как: "отправить сообщение :+
с аргументом [s] (42
) получателю 3.14
".
Ruby - это объектно-ориентированный язык. В объектно-ориентированном программировании мы делаем вещи, заставляя объекты отправлять сообщения другим объектам. Например,
foo.bar(baz)
Значит это self
отправляет сообщение bar
к объекту, полученному путем разыменования локальной переменной foo
, передавая объект, полученный разыменованием локальной переменной baz
в качестве аргумента. (При условии, что foo
а также baz
являются локальными переменными. Они также могут быть отправкой сообщений, поскольку Ruby позволяет вам исключить получателя, если он self
и список аргументов, если он пуст. Обратите внимание, что это будет статически известно синтаксическому анализатору на данном этапе, однако, поскольку локальные переменные создаются статически во время синтаксического анализа.)
В вашем коде есть несколько отправок сообщений:
a + b
отправляет сообщение +
к объекту в переменной a
передача объекта в переменную b
puts add 1, 2
отправляет сообщение add
в self
передавая буквальные целые числа 1
а также 2
в качестве аргумента, затем отправляет сообщение puts
в self
Передав результат вышеприведенного сообщения отправьте в качестве аргумента.
Обратите внимание, что это не имеет ничего общего с Object#send
/ Object#public_send
, Эти два являются отражающими методами, которые позволяют вам определять сообщение динамически, а не статически в исходном коде. Как правило, они реализуются внутренне, делегируя ту же частную внутреннюю подпрограмму времени выполнения, которой делегирует интерпретатор AST. Не наоборот. Переводчик не звонит Object#send
(в противном случае, вы можете настроить правила поиска методов в Ruby с помощью monkey-patching Object#send
, что вы можете легко попробовать это не так), а оба Object#send
и интерпретатор вызывает ту же самую частную внутреннюю деталь реализации.