今日、私が学んだこと
ここ1年ほど Ruby のパーサーがもりあがっているので、 lrama の作者を講師にした 通称ドラゴンブックの「コンパイラ」読書会 に参加しています。
4.8.1 節の「競合解消のための優先順位と結合性」を読み感想を言い合う場で、 以下のような話になった。(細かいとろこは本を読んでくれ
- id + id * id
+
は*
より優先度が低いため shift
- id * id + id
*
は*
より優先度が高いため reduce
- id + id + id
+
は左結合なので reduce
- id = id = id
=
は右結合なので shift
ここでそういえば Ruby ではどう動くのだろうかとおもい、次の式をirbで書いてみた
0 - i = 1 + j = 2 * 3 #=> -7
とまあ普通(?)の答えが返ってきた。
でも上のパーサーの条件考えてみるとそもそもこのコードはほかのプログラミング言語で
パースできるのだろうかと思い、やってみた。
$ python -c '0 - j = 1 + j = 2 * 3'
File "<string>", line 1
0 - j = 1 + j = 2 * 3
^^^^^
SyntaxError: cannot assign to expression
$ perl -e '0 - j = 1 + j = 2 * 3'
Can't modify addition (+) in scalar assignment at -e line 1, at EOF
Execution of -e aborted due to compilation errors.
$ lua -e '0 - j = 1 + j = 2 * 3'
lua: (command line):1: unexpected symbol near '0'
$ crystal eval '0 - j = 1 + j = 2 * 3'
syntax error in eval:1
Error: unexpected token: "="
$ ruby -e '0 - j = 1 + j = 2 * 3'
$
:thinkingface:???とにかく Ruby は通るが他のプログラミング言語で通らないことがわかった。
さいごに Ruby の AST がどうなってるのかとりあずみてみた。
$ ruby -e 'pp RubyVM::AbstractSyntaxTree.parse "0 - j = 1 + j = 2 * 3"'
(SCOPE@1:0-1:21
tbl: [:j]
args: nil
body:
(OPCALL@1:0-1:21 (LIT@1:0-1:1 0) :-
(LIST@1:4-1:21
(LASGN@1:4-1:21 :j
(OPCALL@1:8-1:21 (LIT@1:8-1:9 1) :+
(LIST@1:12-1:21
(LASGN@1:12-1:21 :j (OPCALL@1:16-1:21 (LIT@1:16-1:17 2) :* (LIST@1:20-1:21 (LIT@1:20-1:21 3) nil)))
nil))) nil)))
まあわかるような木構造になってますね。 それはそうとパーサーどうなってるんですかねえという話でおわり。