6

I am trying to learn the Ruby lexer and parser (whitequark parser) to know more about the procedure to further generate machine code from a Ruby script.

On parsing the following Ruby code string.

def add(a, b)
    return a + b
end

puts add 1, 2

It results in the following S-expression notation.

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

Can anyone please explain me the definition of the :send keyword in the resultant S-expression notation?

pravj
  • 300
  • 3
  • 10
  • 4
    I have no idea if this is right, but if I had to guess, `send` is just Object#send, which invokes a method. – Josh Brody Jun 01 '17 at 05:50
  • 1
    _Sidenote:_ if you want to examine Ruby parser’s output, start with cleaning up your ruby code, e.g. remove a redundant `return` from the method body, it will make the generated AST cleaner. And yes, it’s [`Object#send`](https://ruby-doc.org/core/Object.html#method-i-send). – Aleksei Matiushkin Jun 01 '17 at 06:14

2 Answers2

5

Ruby is built on top of “everything is an object” paradigm. That said, everything, including numbers, is an object.

Operators, as we see them in plain ruby code, are nothing but a syntactic sugar for respective object’s methods calls:

3.14.+(42)
#⇒ 45.14

The above is exactly how Ruby treats 3.14 + 42 short notation. It, in turn, might be written using generic Object#send:

3.14.send :+, 42
#⇒ 45.14

The latter should be read as: “send the message :+ with argument[s] (42) to the receiver 3.14.”

Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
  • 1
    More precisely, `3.14.send(:+, 42)` sends the message `:+` with argument `42` to the receiver. The sender does not know, whether `:+` resolves to a method or not. – Stefan Jun 01 '17 at 11:23
4

Ruby is an object-oriented language. In object-oriented programming, we do stuff by having objects send messages to other objects. For example,

foo.bar(baz)

means that self sends the message bar to the object obtained by dereferencing the local variable foo, passing the object obtained by dereferencing the local variable baz as argument. (Assuming that foo and baz are local variables. They could also be message sends, since Ruby allows you to leave out the receiver if it is self and the argument list if it is empty. Note that this would be statically known by the parser at this point, however, since local variables are created statically at parse time.)

In your code, there are several message sends:

a + b

sends the message + to the object in variable a passing the object in variable b

puts add 1, 2

sends to message add to self passing the literal integers 1 and 2 as arguments, then sends the message puts to self passing the result of the above message send as an argument.

Note that this has nothing to do with Object#send / Object#public_send. Those two are reflective methods that allow you to specify the message dynamically instead of statically in the source code. They are typically implemented internally by delegating to the same private internal runtime routine that the AST interpreter delegates to. Not the other way around. The interpreter does not call Object#send (otherwise, you could customize method lookup rules in Ruby by monkey-patching Object#send, which you can easily try is not the case), rather both Object#send and the interpreter call the same private internal implementation detail.

Stefan
  • 109,145
  • 14
  • 143
  • 218
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 1
    _"The interpreter does **not** call Object#send"_ – this was actually very confusing for me when I started learning Ruby. I was expecting `Object#send` to handle every single method invocation. – Stefan Jun 01 '17 at 11:28
  • 1
    That would actually be pretty cool, since you could then re-define what "method invocation" means from within the language! – Jörg W Mittag Jun 01 '17 at 15:36