- 目次ページに戻る / 前 / 次
- 前回からの差分をまとめて見る
真偽判定の変更(コード生成)
while 文と case 文では 条件式の評価結果を 1 と比較して真偽を判定していました。 疑似コードで書くとこう。
# 修正前 if 条件式の評価結果 == 1 真と判定 => while_true ラベルにジャンプ else 偽と判定 => end_while ラベルにジャンプ(ループを抜ける) end label while_true ループの処理本体 label end_while
この場合、たとえば条件式の評価結果が 2 だった場合も偽になってしまいます。 ちょっと違和感がありますね。
というわけで、次のように 0 と比較する形に変更しました。
# 修正後 if 条件式の評価結果 == 0 偽と判定 => end_while ラベルにジャンプ(ループを抜ける) else 真と判定 => while_true ラベルにジャンプ end label while_true ループの処理本体 label end_while
この方が整数から真偽値へのマッピングのルールとして筋が良い気がします。少なくとも C や JavaScript などに近い挙動にはなります。
さらに、この修正により、真だった場合の while_true
ラベルへのジャンプが不要(すぐ次のラベルにジャンプしている)になります。これも消してしまえるので、最終的に次のようになりました。
if 条件式の評価結果 == 0 偽 => end_while ラベルにジャンプ(ループを抜ける) end ループの処理本体 label end_while
コンパイラのコードもコンパイラが生成するコードもちょっとだけコンパクトに。
予約語の判定の改善(レキサ)
予約語と識別子を切り出している箇所(修正前)です。
while pos < src.size rest = src[pos .. -1] case rest # ... # 予約語の判定 when /\A(func|set|var|call_set|call|return|case|while|_cmt|_debug)[^a-z_]/ str = $1 tokens << Token.new(:kw, str) pos += str.size # ... # 識別子の判定 when /\A([a-z_][a-z0-9_]*)/ str = $1 tokens << Token.new(:ident, str) pos += str.size else # ... end end
この、予約語用の正規表現の [^a-z_]
の部分がなんか微妙で、なんとかできないかなという気持ちが前からありました。
そこで、次のように修正しました。
--- a/vglexer.rb +++ b/vglexer.rb @@ -1,5 +1,10 @@ require_relative "./common" +KEYWORDS = [ + "func", "set", "var", "call_set", "call", "return", "case", "while", + "_cmt", "_debug" +] + def tokenize(src) tokens = [] @@ -19,10 +24,6 @@ def tokenize(src) str = $1 tokens << Token.new(:str, str) pos += str.size + 2 - when /\A(func|set|var|call_set|call|return|case|while|_cmt|_debug)[^a-z_]/ - str = $1 - tokens << Token.new(:kw, str) - pos += str.size when /\A(-?[0-9]+)/ str = $1 tokens << Token.new(:int, str.to_i) @@ -33,7 +34,8 @@ def tokenize(src) pos += str.size when /\A([a-z_][a-z0-9_]*)/ str = $1 - tokens << Token.new(:ident, str) + type = KEYWORDS.include?(str) ? :kw : :ident + tokens << Token.new(type, str) pos += str.size else p_e rest[0...100]
曖昧さが減り、安定感のあるコードになったと思います。パターンの考慮漏れについての不安が小さいコードになったというか。
これは正規表現が使えない(標準ライブラリ等に含まれていない)言語に移植していたときに思いついた方法で、それを Ruby 版にフィードバックしたものです。
ちなみに、このように書けるのは 予約語のパターンが識別子のパターンに含まれるためですね。 その前提が崩れるとダメですが、まあ、問題ないでしょう。
その他
- パーサ: loop を while で書き換え
- トークンの値を取り出す処理を
Token#get_value
に抽出