Что означает ключевое слово 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 и интерпретатор вызывает ту же самую частную внутреннюю деталь реализации.

Другие вопросы по тегам