小ネタ: Ruby 2.5.0 の細かい構文的変化

サーバーのRubyバージョンを2.4.5から2.5.3に上げようとしたところ、構文エラーが発生しました。その構文エラーは devise に由来しているもので、 #4668 で修正されたものでした。最小化すると、以下のようなコードが構文エラーになるようになっていました。

def foo(options, &block); end
foo a: [ ] {}

devise 自体はアップデートすればいいとして、せっかくなのでこの原因を追跡してみました。構文エラーなので parse.y (2.4.5, 2.5.3) を読んでいきます。字句解析器がステートフルなので完全に理詰めで詰めるのは難しいですが、2行目の導出列はおそらく以下のようになるだろうと見当をつけました。


program
top_compstmt
top_stmts opt_terms
top_stmt opt_terms
stmt opt_terms
expr opt_terms
command_call opt_terms
command opt_terms
fcall command_args cmd_brace_block opt_terms
operation command_args cmd_brace_block opt_terms
tIDENTIFIER command_args cmd_brace_block opt_terms
tIDENTIFIER call_args cmd_brace_block opt_terms
tIDENTIFIER assocs opt_block_arg cmd_brace_block opt_terms
tIDENTIFIER assoc opt_block_arg cmd_brace_block opt_terms
tIDENTIFIER tLABEL arg_value opt_block_arg cmd_brace_block opt_terms
tIDENTIFIER tLABEL arg opt_block_arg cmd_brace_block opt_terms
tIDENTIFIER tLABEL primary opt_block_arg cmd_brace_block opt_terms
tIDENTIFIER tLABEL tLBRACK aref_args ']' opt_block_arg cmd_brace_block opt_terms
tIDENTIFIER tLABEL tLBRACK none ']' opt_block_arg cmd_brace_block opt_terms
tIDENTIFIER tLABEL tLBRACK ']' opt_block_arg cmd_brace_block opt_terms
tIDENTIFIER tLABEL tLBRACK ']' none cmd_brace_block opt_terms
tIDENTIFIER tLABEL tLBRACK ']' tLBRACE_ARG brace_body '}' opt_terms
tIDENTIFIER tLABEL tLBRACK ']' tLBRACE_ARG opt_block_param compstmt '}' opt_terms
tIDENTIFIER tLABEL tLBRACK ']' tLBRACE_ARG none compstmt '}' opt_terms
tIDENTIFIER tLABEL tLBRACK ']' tLBRACE_ARG compstmt '}' opt_terms
tIDENTIFIER tLABEL tLBRACK ']' tLBRACE_ARG stmts opt_terms '}' opt_terms
tIDENTIFIER tLABEL tLBRACK ']' tLBRACE_ARG none opt_terms '}' opt_terms
tIDENTIFIER tLABEL tLBRACK ']' tLBRACE_ARG '}' opt_terms
tIDENTIFIER tLABEL tLBRACK ']' tLBRACE_ARG '}'

このように見当をつけた上で、エラーメッセージを見ると、

test.rb:2: syntax error, unexpected '{', expecting end-of-input
foo a: [ ] {}

なので、 tLBRACE_ARG ではなく '{' という終端記号を期待していることがわかります。この2つはどちらも同じ { という文字から、字句解析器の状態に応じて出ています。ということは、状態の計算を調べると良さそうです。

上記の終端記号列が出るはずという推測から字句解析器 (parse_yylex 関数) をつまみ食いしてみます。

すると、字句解析器の状態のうち lex_state が以下のような遷移をとっていると推測がつきます。


(EXPR_BEG)
foo -> tIDENTIFIER (EXPR_END|EXPR_LABEL)
a: -> tLABEL (EXPR_ARG|EXPR_LABELED)
[ -> tLBRACK (EXPR_BEG|EXPR_LABEL)
] -> ']' (EXPR_ENDARG (2.4.5) EXPR_END (2.5.3))
...

これで原因の見当がつきました。 ] を入力したときの次の状態の計算 (2.4.5, 2.5.3) がこのコミットで変更されています。これはこのチケットを修正するために入ったもののようでした。

試しに、このコミットの前後のソースをビルドして最初の例を実行してみたところ、確かにこのコミットの後で構文解析に失敗するようになったことが確認できました。

仕組みがわかったので、気持ちよく年越しができそうです。皆さま、よいお年を。

Wantedly, Inc.'s job postings
Anonymous
1d0691c8 ddbf 41aa 9e3b 9a0c8e0c58ab?1544074083
8489e856 647d 43c6 b196 fb8336d1873e?1529041022
59b613f5 58d8 4483 818e 751dd7109c9d
A21ef78b 68fd 4dce a0c3 c94916bf3019
55fed19d 10d0 422f b08e 8765acf21514
11 Likes
Anonymous
1d0691c8 ddbf 41aa 9e3b 9a0c8e0c58ab?1544074083
8489e856 647d 43c6 b196 fb8336d1873e?1529041022
59b613f5 58d8 4483 818e 751dd7109c9d
A21ef78b 68fd 4dce a0c3 c94916bf3019
55fed19d 10d0 422f b08e 8765acf21514
11 Likes

Weekly ranking

Show other rankings
If this story triggered your interest, go ahead and visit them to learn more

Page top icon