Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Làm Chủ Phân Tích Tĩnh Ruby: Trích Xuất Phương Thức & Tham Số Với Gem Parser

Giả sử bạn muốn parse mã nguồn tìm tất cả phương thức, nơi định nghĩa & tham số gì.

Làm thế nào?

Ý tưởng đầu có thể viết regexp…

Nhưng có cách tốt hơn?

Làm Chủ Phân Tích Tĩnh Ruby: Trích Xuất Phương Thức & Tham Số Với Gem Parser

Có!

Phân tích tĩnh (Static analysis) là kỹ thuật dùng khi cần trích xuất thông tin từ chính mã nguồn.

Được làm bằng cách chuyển mã nguồn thành tokens (parsing).

Bắt đầu ngay!

Dùng Gem Parser

Ruby có parser trong thư viện chuẩn, tên Ripper. Đầu ra khó làm việc nên thích dùng gem parser tuyệt vời. Rubocop dùng gem này làm phép màu.

Gem này cũng có binary dùng parse code trực tiếp xem parse tree kết quả.

Ví dụ:

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

Đầu ra trông như:

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

Hữu ích nếu muốn hiểu Ruby parse code như thế nào. Nhưng muốn tạo công cụ phân tích riêng phải đọc file nguồn, parse và duyệt cây sinh ra.

Ví dụ:

require 'parser/current'
code = File.read('app.rb')
parsed_code = Parser::CurrentRuby.parse(code)

Parser trả về AST (Abstract Syntax Tree - Cú pháp trừu tượng) code. Đừng ngại tên, nó đơn giản hơn nghe 🙂

Duyệt AST

Sau khi parse code dùng gem parser cần duyệt AST kết quả.

Làm bằng cách tạo class kế thừa AST::Processor.

Ví dụ:

class Processor < AST::Processor
end

Rồi khởi tạo class & gọi .process:

ast = Processor.new
ast.process(parsed_code)

Cần định nghĩa vài phương thức on_. Các phương thức này tương ứng tên node trong AST.

Khám phá phương thức cần định nghĩa, thêm handler_missing vào Processor class. Cũng cần 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

Đây là vị trí chúng ta:

Có Ruby AST và processor cơ bản, chạy code sẽ thấy node types cho AST.

Bây giờ:

Cần triển khai tất cả phương thức on_ muốn dùng. Ví dụ, muốn tất cả tên phương thức instance kèm số dòng:

def on_def(node)
  line_num = node.loc.line
  method_name = node.children[0]
  puts "Found #{method_name} at line #{line_num}"
end

Chạy chương trình bây giờ in tất cả tên phương thức tìm thấy.

Kết Luận

Xây dựng công cụ phân tích tĩnh Ruby không khó như vẻ bề ngoài. Muốn ví dụ hoàn chỉnh hơn xem gem class_indexer của mình. Bây giờ lượt bạn tạo công cụ riêng!

Xin vui lòng chia sẻ bài viết nếu thích! 🙂