【问题标题】:What is the meaning of the 'send' keyword in Ruby's AST?Ruby AST 中的“send”关键字是什么意思?
【发布时间】:2017-06-01 05:44:34
【问题描述】:

我正在尝试学习 Ruby 词法分析器和解析器 (whitequark parser),以了解更多关于从 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 表达式符号中的定义吗?

【问题讨论】:

  • 我不知道这是否正确,但如果我不得不猜测,send 只是 Object#send,它调用了一个方法。
  • 旁注:如果你想检查 Ruby 解析器的输出,首先清理你的 ruby​​ 代码,例如从方法体中删除一个多余的return,它会使生成的AST更干净。是的,它是Object#send

标签: ruby parsing abstract-syntax-tree s-expression ruby-ripper


【解决方案1】:

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

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

3.14.+(42)
#⇒ 45.14

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

3.14.send :+, 42
#⇒ 45.14

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

【讨论】:

  • 更准确地说,3.14.send(:+, 42) 将带有参数42 的消息:+ 发送给接收者。发送者不知道:+ 是否解析为方法。
【解决方案2】:

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

foo.bar(baz)

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

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

a + b

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

puts add 1, 2

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

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

【讨论】:

  • “解释器确实调用 Object#send”——当我开始学习 Ruby 时,这实际上让我很困惑。我期待Object#send 处理每一个方法调用。
  • 这实际上很酷,因为您可以从语言中重新定义“方法调用”的含义!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-14
  • 2010-10-29
  • 2011-10-28
  • 1970-01-01
相关资源
最近更新 更多