Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyでパーサーを構築する方法

構文解析は、一連の文字列を理解し、それらを理解できるものに変換する技術です。正規表現を使用することもできますが、必ずしもその仕事に適しているとは限りません。

たとえば、HTMLを正規表現で解析することはおそらく良い考えではないことは一般的な知識です。

Rubyには、この作業を実行できるnokogiriがありますが、独自のパーサーを作成することで多くのことを学ぶことができます。始めましょう!

Rubyでの解析

パーサーの中核はStringScannerです クラス。

このクラスは、文字列のコピーと位置ポインタを保持します。ポインタを使用すると、特定のトークンを検索するために文字列をトラバースできます。

使用する方法は次のとおりです。

  • .peek
  • .scan_until
  • .getch

もう1つの便利な方法は、 .scanです。 (までなし)

StringScannerが利用できない場合は、require 'strscan'を追加してみてください

このクラスがどのように機能するかを理解できるように、ドキュメントとして2つのテストを作成しました。

describe StringScanner do
  let (:buff) { StringScanner.new "testing" }

  it "can peek one step ahead" do
    expect(buff.peek 1).to eq "t"
  end

  it "can read one char and return it" do
    expect(buff.getch).to eq "t"
    expect(buff.getch).to eq "e"
  end
end

このクラスについて注意すべき重要な点の1つは、一部のメソッドが位置ポインターを進めることです( getch、scan )、他の人はしません(覗く )。いつでもスキャナーを検査できます( .inspect を使用) またはp )どこにあるかを確認します。

パーサークラス

パーサークラスは、ほとんどの作業が行われる場所です。解析するテキストのスニペットで初期化し、そのためのStringScannerを作成して、解析メソッドを呼び出します。

def initialize(str)
  @buffer = StringScanner.new(str)
  @tags   = []
  parse
end

テストでは、次のように定義します。

let(:parser) { Parser.new "<body>testing</body> <title>parsing with ruby</title>" }

このクラスがどのように機能するかについては少し詳しく説明しますが、最初にプログラムの最後の部分を見てみましょう。

タグクラス

このクラスは非常に単純で、主に結果を解析するためのコンテナとデータのクラスとして機能します。

class Tag
  attr_reader :name
  attr_accessor :content

  def initialize(name)
    @name = name
  end
end

解析しましょう!

何かを解析するには、入力テキストを調べてパターンを見つける必要があります。たとえば、HTMLコードの形式は次のとおりです。

<tag>contents</tag>

ここで識別できる2つの異なるコンポーネント、タグ名とタグ内のテキストが明らかにあります。 BNF表記を使用して形式文法を定義すると、次のようになります。

tag = <opening_tag> <contents> <closing_tag>
opening_tag = "<" <tag_name> ">"
closing_tag = "</" <tag_name> ">"

StringScannersのピークを使用します 入力バッファの次のシンボルが開始タグであるかどうかを確認します。その場合は、 find_tagと呼びます。 およびfind_content パーサークラスのメソッド:

def parse_element
  if @buffer.peek(1) == '<'
    @tags << find_tag
    last_tag.content = find_content
  end
end

find_tag メソッドは:

  • 開始タグ文字を「消費」する
  • 終了記号(「>」)が見つかるまでスキャンします
  • タグ名を使用して新しいTagオブジェクトを作成して返します

これがコードです。切り刻む方法に注意してください。 最後の文字。これは、 scan_until が原因です 結果に「>」が含まれていますが、これは望ましくありません。

def find_tag
  @buffer.getch
  tag = @buffer.scan_until />/
  Tag.new(tag.chop)
end

次のステップは、タグ内のコンテンツを見つけることです。scan_untilメソッドは位置ポインターを適切な場所に進めるため、これはそれほど難しいことではありません。もう一度scan_untilを使用して、終了タグを見つけ、タグの内容を返します。

Rubyでパーサーを構築する方法

def find_content
  tag = last_tag.name
  content = @buffer.scan_until /<\/#{tag}>/
  content.sub("</#{tag}>", "")
end

parse_elementを呼び出すだけです。 入力バッファにタグが見つからなくなるまでループします。

def parse
  until @buffer.eos?
    skip_spaces
    parse_element
  end
end

完全なコードは、https://github.com/matugm/simple-parserにあります。別のタグ内のタグを処理できる拡張バージョンの「nested_tags」ブランチを確認することもできます。

結論

パーサーの作成は興味深いトピックであり、時にはかなり複雑になることもあります。

独自のパーサーを最初から作成したくない場合は、いわゆる「パーサジェネレータ」の1つを使用できます。 Rubyにはツリートップとパーレットがあります。


  1. Rubyでファイルを読み書きする方法(例付き)

    今日は、Rubyでファイルを読み書きして、コンテンツを抽出し、新しいファイルを作成し、必要な情報を見つける方法を学びます。 これから説明します : コンテンツ 1Rubyでファイルを読み取る方法 2Rubyでファイルに書き込む方法 3つのRubyファイルメソッド 4つのディレクトリ操作 5FileUtilsモジュールの使用方法 6まとめ 6.1関連 やってみましょう! Rubyでファイルを読み取る方法 次のようにRubyでファイルを読み取ることができます: ファイルを開く 、open メソッド。 ファイルを読む 、ファイル全体、行ごと、または特定のバイト数。 ファイルを

  2. SwiftUIでデザインシステムを構築する方法

    1 つの製品をサポートする設計システムを構築するのは簡単ではありません。スケーラビリティのために堅牢で柔軟でなければなりません。困難ではありますが、多くの優れたリソースが、チームが視覚的にもプログラム的にも優れたシステムを構築するのに役立つ有用な原則とアプローチを共有しています。この記事は、SwiftUI で優れたシステムを構築することに焦点を当てることで、彼らの肩に寄り添い、手付かずの地に貢献しようとします。 . なぜこの記事を書くのか ニューヨークの ITP での最初の夏に、幸運にも Line Break Studio で iOS 開発者のインターンとして働く機会がありました。私が割り当