Ruby でこの文字列をトークン化するにはどうすればよいですか?

概要

この文字列があります:

%{Children^10 Health "sanitation management"^5}

そして、これをハッシュの配列にトークン化するために変換したいと思います。

[{:keywords=>"children", :boost=>10}, {:keywords=>"health", :boost=>nil}, {:keywords=>"sanitation management", :boost=>5}]

StringScanner と Syntax gem については知っていますが、両方の十分なコード例が見つかりません。

何かヒントはありますか?

解決策

ガス氏が言ったように、実際の言語の場合はレクサーが最適です。ただし、完全な言語が例と同じくらい複雑な場合は、次の簡単なハックを使用できます。

irb> text = %{Children^10 Health "sanitation management"^5}
irb> text.scan(/(?:(\w+)|"((?:\\.|[^\\"])*)")(?:\^(\d+))?/).map do |word,phrase,boost|
       { :keywords => (word || phrase).downcase, :boost => (boost.nil? ? nil : boost.to_i) }
     end
#=> [{:boost=>10, :keywords=>"children"}, {:boost=>nil, :keywords=>"health"}, {:boost=>5, :keywords=>"sanitation management"}]

正規言語を解析しようとしている場合は、この方法で十分です。ただし、言語を非正規にするためにさらに多くの複雑な処理は必要ありません。

正規表現の簡単な内訳:

String#scan(regex) は、正規表現を文字列と可能な限り何度も照合し、「一致」の配列を出力します。正規表現にキャプチャ用の括弧が含まれている場合、「一致」はキャプチャされた項目の配列です。つまり、match[0]、match[1] などになります。文字列の一部と一致しないキャプチャ用の括弧は、結果の「一致」には nil エントリが含まれます。

次に #map はこれらの一致を取得し、ブロック マジックを使用してキャプチャされた各用語を異なる変数に分割し ( do |match| ; word,phrase,boost = *match を実行することもできます)、その後、必要なハッシュを作成します。単語またはフレーズの 1 つだけが nil になります。両方とも入力に対して照合できないため、(word || フレーズ) は非 nil を返し、#downcase はそれをすべて小文字に変換します。 boost.to_i は文字列を整数に変換し、(boost.nil? ? nil : boost.to_i) は nil ブーストが nil のままであることを保証します。