5

我正在尝试学习 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))))

谁能解释一下生成的 S 表达式符号中:send关键字的定义?

4

2 回答 2

5

Ruby 建立在“一切都是对象”范式之上。也就是说,包括数字在内的一切都是对象。

运算符,正如我们在普通的 ruby​​ 代码中看到的那样,只不过是各个对象方法调用的语法糖:

3.14.+(42)
#⇒ 45.14

以上正是 Ruby 处理3.14 + 42短符号的方式。反过来,它可以使用 generic 编写Object#send

3.14.send :+, 42
#⇒ 45.14

后者应理解为:“将:+带有参数 [s] ( 42) 的消息发送给接收者3.14。”

于 2017-06-01T06:17:32.533 回答
4

Ruby 是一种面向对象的语言。在面向对象编程中,我们通过让对象向其他对象发送消息来做事。例如,

foo.bar(baz)

表示self将消息发送bar到通过取消引用局部变量foo获得的对象,将通过取消引用局部变量获得的对象作为参数传递baz。(假设foobaz是局部变量。它们也可以是消息发送,因为 Ruby 允许您省略接收者(如果是)self,如果参数列表为空则省略参数列表。请注意,此时解析器将静态知道这一点,但是,由于局部变量是在解析时静态创建的。)

在您的代码中,有几个消息发送:

a + b

将消息发送+到变量中a的对象传递变量中的对象b

puts add 1, 2

发送到消息addself传递文字整数12作为参数,然后将消息发送putsself传递上述消息的结果作为参数发送。

请注意,这与/无关。这两个是反射方法,允许您在源代码中动态而不是静态地指定消息。它们通常通过委托给 AST 解释器委托给的同一个私有内部运行时例程在内部实现。不是反过来。解释器调用(否则,您可以通过 monkey-patching 自定义 Ruby 中的方法查找规则,您可以轻松尝试并非如此),而是两者和解释器调用相同的私有内部实现细节。Object#sendObject#public_sendObject#sendObject#sendObject#send

于 2017-06-01T07:41:12.800 回答