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

Ruby의 정적 분석

소스 코드를 구문 분석하여 모든 메서드, 메서드가 정의된 위치 및 사용하는 인수를 찾고 싶다고 가정해 보겠습니다.

어떻게 할 수 있나요?

첫 번째 아이디어는 정규 표현식을 작성하는 것입니다...

하지만 더 좋은 방법이 있습니까?

Ruby의 정적 분석

네!

정적 분석 소스 코드 자체에서 정보를 추출해야 할 때 사용할 수 있는 기술입니다.

이는 소스 코드를 토큰으로 변환(파싱)하여 수행됩니다.

바로 시작하겠습니다!

파서 젬 사용

Ruby에는 표준 라이브러리에서 사용할 수 있는 파서가 있으며 이름은 Ripper입니다. 출력은 작업하기 어렵기 때문에 저는 환상적인 파서 gem을 사용하는 것을 선호합니다. Rubocop은 이 보석을 마법으로 사용합니다.

이 보석에는 일부 코드를 직접 구문 분석하고 결과 구문 분석 트리를 보는 데 사용할 수 있는 바이너리도 포함되어 있습니다.

예시입니다 :

ruby-parse -e '%w(hello world).map { |c| c.upcase }'

출력은 다음과 같습니다.

(block
  (send
    (array
      (str "hello")
      (str "world")) :map)
  (args
    (arg :c))
  (send
    (lvar :c) :upcase))

Ruby가 일부 코드를 구문 분석하는 방법을 이해하려는 경우 유용할 수 있습니다. 그러나 자신만의 분석 도구를 만들고 싶다면 소스 파일을 읽고 구문 분석한 다음 생성된 트리를 탐색해야 합니다.

:

require 'parser/current'

code = File.read('app.rb')
parsed_code = Parser::CurrentRuby.parse(code)

파서는 코드의 AST(추상 구문 트리)를 반환합니다. 이름에 너무 겁먹지 마세요, 생각보다 간단합니다 🙂

AST 순회

이제 파서를 사용하여 코드를 파싱했습니다. 결과 AST를 통과하는 데 필요한 gem.

AST::Processor에서 상속하는 클래스를 만들어 이를 수행할 수 있습니다. .

:

class Processor < AST::Processor
end

그런 다음 이 클래스를 인스턴스화하고 .process를 호출해야 합니다. 방법:

ast = Processor.new
ast.process(parsed_code)

몇 가지 on_을 정의해야 합니다. 행동 양식. 이 메소드는 AST의 노드 이름에 해당합니다.

정의해야 하는 메소드를 찾으려면 handler_missing을 추가할 수 있습니다. 프로세서 클래스에 대한 메서드입니다. on_begin도 필요합니다. 방법.

class Processor < AST::Processor
  def on_begin(node)
    node.children.each { |c| process(c) }
  end

  def handler_missing(node)
    puts "missing #{node.type}"
  end
end

여기가 우리가 있는 곳입니다 :

Ruby AST와 기본 프로세서가 있으며 이 코드를 실행하면 AST의 노드 유형이 표시됩니다.

지금 :

모든 on_을 구현해야 합니다. 사용하려는 방법. 예를 들어 모든 인스턴스 메서드 이름과 줄 번호를 원하면 다음과 같이 할 수 있습니다.

def on_def(node)
  line_num    = node.loc.line
  method_name = node.children[0]

  puts "Found #{method_name} at line #{line_num}"
end

지금 프로그램을 실행하면 발견된 모든 메소드 이름을 인쇄해야 합니다.

결론

Ruby 정적 분석 도구를 구축하는 것은 보기보다 어렵지 않습니다. 더 완전한 예제를 원하면 내 class_indexer gem을 살펴보세요. 이제 나만의 도구를 만들 차례입니다!

이 게시물을 공유하세요. 당신이 그것을 즐겼다면! 🙂