- Ruby代码的运行
在1.9及以后版本的Ruby中,原代码运行要经过好几个步骤:
分词:得到词条
解析:得到抽象语法树(AST)
编译:使用YARV从AST转化到字节码
- 关于分词
可以使用Ripper查看Ruby分词的结果:
require 'ripper'
code = <
[[[1, 0], :on_int, "10"],
[[1, 2], :on_period, "."],
[[1, 3], :on_ident, "times"],
[[1, 8], :on_sp, " "],
[[1, 9], :on_kw, "do"],
[[1, 11], :on_sp, " "],
[[1, 12], :on_op, "|"],
[[1, 13], :on_ident, "n"],
[[1, 14], :on_op, "|"],
[[1, 15], :on_ignored_nl, "\n"],
[[2, 0], :on_sp, " "],
[[2, 2], :on_ident, "puts"],
[[2, 6], :on_sp, " "],
[[2, 7], :on_ident, "n"],
[[2, 8], :on_nl, "\n"],
[[3, 0], :on_kw, "end"],
[[3, 3], :on_nl, "\n"]]
- 关于解析
Ruby使用Bison,在构建过程中创建解析器。
LALR算法:向前查看反向最右推导
解析后的结果是AST语法树,可以通过Ripper查看:
require 'ripper'
require 'pp'
code = <
[:program,
[[:binary,
[:@int, "2", [1, 0]],
:+,
[:binary, [:@int, "2", [1, 4]], :*, [:@int, "3", [1, 8]]]]]]
=> [:program, [[:binary, [:@int, "2", [1, 0]], :+, [:binary, [:@int, "2", [1, 4]], :*, [:@int, "3", [1, 8]]]]]]
- 关于编译
RubyVM::InstructionSequence可以帮助显示Ruby编译后的YARV指令。
code = <
== disasm: #@>================================
== catch table
| catch type: break st: 0002 ed: 0008 sp: 0000 cont: 0008
|------------------------------------------------------------------------
0000 trace 1 ( 1)
0002 putobject 10
0004 send , , block in
0008 leave
== disasm: #@>=======================
== catch table
| catch type: redo st: 0002 ed: 0010 sp: 0000 cont: 0002
| catch type: next st: 0002 ed: 0010 sp: 0000 cont: 0010
|------------------------------------------------------------------------
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 2] n
0000 trace 256 ( 1)
0002 trace 1 ( 2)
0004 putself
0005 getlocal_OP__WC__0 2
0007 opt_send_without_block ,
0010 trace 512 ( 3)
0012 leave ( 2)
这段代码里有个do...end块,所以在YARV指令里可以看到本地表(local table)的存在。