Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Ruby로 파서를 만드는 방법

구문 분석은 일련의 문자열을 이해하고 우리가 이해할 수 있는 것으로 변환하는 기술입니다. 정규 표현식을 사용할 수 있지만 항상 작업에 적합한 것은 아닙니다.

예를 들어, 정규 표현식으로 HTML을 구문 분석하는 것은 좋은 생각이 아니라는 것은 상식입니다.

Ruby에는 이 작업을 수행할 수 있는 nokogiri가 있지만 자신만의 파서를 구축하여 많은 것을 배울 수 있습니다. 시작하겠습니다!

Ruby로 구문 분석

파서의 핵심은 StringScanner입니다. 수업.

이 클래스는 문자열의 복사본과 위치 포인터를 보유합니다. 포인터를 사용하면 특정 토큰을 검색하기 위해 문자열을 탐색할 수 있습니다.

우리가 사용할 방법은 다음과 같습니다:

  • .피크
  • .scan_until
  • .get

또 다른 유용한 방법은 .scan입니다. (까지 제외).

참고 :

StringScanner를 사용할 수 없는 경우 require 'strscan'을 추가해 보십시오.

이 클래스의 작동 방식을 이해할 수 있도록 두 가지 테스트를 문서로 작성했습니다.

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

이 클래스에서 주의해야 할 한 가지 중요한 점은 일부 메서드가 위치 포인터(가져오기, 스캔 ), 다른 사람들은 그렇지 않습니다( 엿보기 ). 언제든지 스캐너를 검사할 수 있습니다(.inspect 사용). 또는 p ) 위치를 확인합니다.

파서 클래스

파서 클래스는 대부분의 작업이 발생하는 곳입니다. 파싱하려는 텍스트 스니펫으로 이를 초기화하고 이에 대한 StringScanner를 생성하고 parse 메소드를 호출합니다.

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>

여기에서 식별할 수 있는 두 가지 다른 구성 요소가 있습니다. 바로 태그 이름과 태그 내부의 텍스트입니다. BNF 표기법을 사용하여 형식 문법을 정의하면 다음과 같이 보일 것입니다.

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

StringScanner의 피크 입력 버퍼의 다음 기호가 여는 태그인지 확인합니다. 이 경우 find_tag콘텐츠 찾기 Parser 클래스의 메소드:

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

find_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' 브랜치를 볼 수도 있습니다.

결론

파서를 작성하는 것은 흥미로운 주제이며 때때로 꽤 복잡해질 수도 있습니다.

자신만의 파서를 처음부터 만들고 싶지 않다면 소위 '파서 생성기' 중 하나를 사용할 수 있습니다. Ruby에는 나무 꼭대기와 파슬리가 있습니다.