【Ruby】StringScanner を使った字句解析
元気になったよー。
の師匠の返事が
tokenメソッドの責務がよくわからない StringScannerを使うのはいいけど、 正規表現に頼った方法やとスキャンがいっぱい走るし あんまり筋がいいとは思わない。
だったので、改めてStringScannerについて考えてみる
元のコード
字句解析の箇所だけ抜粋
require 'strscan' def lexical_analysis(data) s = StringScanner.new(data) tokens = [] while !s.eos? if s.scan(/<\/\S+?>/) tokens << [s[0].gsub(/[<\/>]/,''), :end_form] elsif s.scan(/<[^>]+?\/>/) tokens << [s[0].gsub(/[<\/>]/,''), :special_form] elsif s.scan(/<\S+?>/) tokens << [s[0].gsub(/[<\/>]/,''), :start_form] elsif s.scan(/[^<]+/) tokens << [s[0], :text] end end tokens end data='<a>huga<b>hello<hoge/>dada</b><c>pupu</c><d/></a>' p lexical_analysis(data)
■実行結果
[["a", :start_form], ["huga", :text], ["b", :start_form], ["hello", :text], ["hoge", :special_form], ["dada", :text], ["b", :end_form], ["c", :start_form], ["pupu", :text], ["c", :end_form], ["d", :special_form], ["a", :end_form]]
確かにこれだと何回もscanが走る。
たとえば今のコードだと
<hoge>
ってコードがあった場合、
s.scan(/<\/\S+?>/) が走ってダメ
s.scan(/<[^>]+?\/>/) が走ってダメ
s.scan(/<\S+?>/) が走ってようやくOK
と、何回もscanしている
そうだ!
”<>”で囲まれているか否かだけをscanで判定して、
囲まれている場合はif文で条件分岐すればどうだろう。
scanの回数が減る。
tokenメソッドの責務がよくわからない
というのはどういう意味だろう。
字句解析をして、タグ要素とテキスト要素に分ける処理は間違っていないはず。
要素として正しい形か判定していないということかな。。?
エラー判定はどこまで細かくするかだけど、、
<>の最初と最後以外に”/”が存在したらエラーにする。
修正したコード
require 'strscan' def lexical_analysis(data) s = StringScanner.new(data) tokens = [] while !s.eos? if s.scan(/<[^>]+>/) if s[0].index("</") tokens << [s[0].gsub(/[<\/>]/,''), :end_form] elsif s[0].index("/>") tokens << [s[0].gsub(/[<\/>]/,''), :special_form] elsif s[0].index("/") raise "incorrect xml tag" else tokens << [s[0].gsub(/[<>]/,''), :start_form] end else s.scan(/[^<]+/) tokens << [s[0], :text] end end tokens end data='<a>huga<b>hello<hoge/>dada</b><c>pupu</c><d/></a>' p lexical_analysis(data)
scanも減ってすっきりした