ここ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 は通るが他のプログラミング言語で通らないことがわかった。

さいごに RubyAST がどうなってるのかとりあずみてみた。

$ 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)))

まあわかるような木構造になってますね。 それはそうとパーサーどうなってるんですかねえという話でおわり。