Ruby は、不必要に構文が複雑になっている。最初期からある構文がまずく (曖昧で), 実装が複雑になっている。その割に独自の利便性の高い構文があるわけではない。CommonLisp のほうが、よほどできることが大きい。
サブセットと割り切って、簡単にした。
けっこう小さなサブセットと割り切ったつもりだが、それでも合わせて3,000行に膨らんでしまった。
parse.y
Bisonファイル 14,000行.
字句解析は 行9,230 辺りから parser_yylex()
関数.
lex.c
gperf コマンドで生成. 入力 = defs/keywords
ファイル
node.h
AST ノードを定める。104 種類, すごい多い。
parse.yy
Bisonファイル 1,200 行程度
lexer.cpp
手書きの字句解析器。1,700行程度
ast.h
ノードを定める。36 種類。
Ruby ではメソッド呼出しになっている include
, extend
, attr_accessor
, attr_reader
, attr_writer
を予約語化
字句解析は, minilang v0.6 では Quex という字句解析器生成系を使ったが、不十分で、結局手書きした。
文脈依存キーワード contextual keywords は、今どき、どのプログラミング言語でも見られる。例えば JavaScript の async
はキーワードではない。C# も互換性維持のために多い。
Ruby の難しさはそこではなく、スクリプトの字面のつながり方だけでは構文が一意に定まらず、コンパイル時に意味を併用しなければならない。C/C++ も同様。(context-free grammar ではない.) 未実装 [incompat]
例えばこの記事「あなたが理解できない,たった一行のRubyのコード(動的言語に対する静的解析の限界)」は完全に誤認しているが、それでも, "/" の曖昧性を解決するためには, f
が変数かどうかを踏まえなければならない。
正規表現リテラルかどうかの判定は、1パス目でやらなければならない。あらゆる文字が含まれうるので、先読みして決めることはできない。
if
式と後置 if
とで挙動が異なる。抽象構文木 (AST) も異なる。
上のスクリプトは、単に未代入のローカル変数の評価はエラーにしたらいいのでは? という話。
それはともかく, Ruby は Lisp-2 で、変数の名前空間と関数のそれが分かれている。変数と同名の関数があってよい (定義できる)。しかし、関数呼出しのカッコを省略できる (仕様がまずい) ので、字面から変数評価か関数呼出しかが区別できない。なので, if
式の条件式のほうは AST node VCALL
(未解決) になっている。
1パス目では確かに, 単に f
と書いたときにどちらの意味か分からないが、構築した AST を静的解析すれば、どちらか分かる。型チェックもここで入れることができる。[[まだ未実装]]