소스 코드를 구문 분석하여 모든 메서드, 메서드가 정의된 위치 및 사용하는 인수를 찾고 싶다고 가정해 보겠습니다.
어떻게 할 수 있나요?
첫 번째 아이디어는 정규 표현식을 작성하는 것입니다...
하지만 더 좋은 방법이 있습니까?
네!
정적 분석 소스 코드 자체에서 정보를 추출해야 할 때 사용할 수 있는 기술입니다.
이는 소스 코드를 토큰으로 변환(파싱)하여 수행됩니다.
바로 시작하겠습니다!
파서 젬 사용
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을 살펴보세요. 이제 나만의 도구를 만들 차례입니다!
이 게시물을 공유하세요. 당신이 그것을 즐겼다면! 🙂