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?
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
.”
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With