parse.y parse.c 負責兩個工作,將 Ruby code 變成 token 後比對 grammar 建立 AST
用 Ripper
看產生的 tokens
# require 'ripper'
# require 'pp'
#
# pp Ripper.lex("def m(a) nil end")
# #=> [[[1, 0], :on_kw, "def", FNAME ],
# [[1, 3], :on_sp, " ", FNAME ],
# [[1, 4], :on_ident, "m", ENDFN ],
# [[1, 5], :on_lparen, "(", BEG|LABEL],
# [[1, 6], :on_ident, "a", ARG ],
# [[1, 7], :on_rparen, ")", ENDFN ],
# [[1, 8], :on_sp, " ", BEG ],
# [[1, 9], :on_kw, "nil", END ],
# [[1, 12], :on_sp, " ", END ],
# [[1, 13], :on_kw, "end", END ]]
Ripper
是用 C 實作的 Ruby extension (參考 https://docs.ruby-lang.org/en/3.2/extension_rdoc.html )
在 ripper.c 可以找到實作
void
InitVM_ripper(void)
{
VALUE Ripper;
Ripper = rb_define_class("Ripper", rb_cObject);
/* version of Ripper */
rb_define_const(Ripper, "Version", rb_usascii_str_new2(RIPPER_VERSION));
rb_define_alloc_func(Ripper, ripper_s_allocate);
rb_define_method(Ripper, "initialize", ripper_initialize, -1);
rb_define_method(Ripper, "parse", ripper_parse, 0);
// ...
ripper.c 是經由 parse.c 產生的檔案,裡面有幾乎相同用來 parse ruby code 的 C function。從 Ripper.lex
的 Ruby code 一路可以看到 ripper_parse, ripper_parse0, ripper_yyparse
呼叫到 yyparse
。單純只研究 parser 的話可以從 ripper 實作來看,少了很多 YARV 的操作。