Computer >> Máy Tính >  >> Lập trình >> Ruby

Phân tích tĩnh trong Ruby

Giả sử bạn muốn phân tích cú pháp mã nguồn của mình để tìm tất cả các phương thức của bạn, nơi chúng được xác định và chúng sử dụng đối số nào.

Làm thế nào bạn có thể làm điều này?

Ý tưởng đầu tiên của bạn có thể là viết một regexp cho nó…

Nhưng có cách nào tốt hơn không?

Phân tích tĩnh trong Ruby

Vâng!

Phân tích tĩnh là một kỹ thuật bạn có thể sử dụng khi cần trích xuất thông tin từ chính mã nguồn.

Điều này được thực hiện bằng cách chuyển đổi mã nguồn thành mã thông báo (phân tích cú pháp).

Hãy bắt tay ngay vào nó!

Sử dụng Đá quý phân tích cú pháp

Ruby có sẵn một trình phân tích cú pháp trên thư viện chuẩn, tên là Ripper. Khó làm việc với đầu ra vì vậy tôi thích sử dụng đá quý phân tích cú pháp tuyệt vời hơn. Rubocop sử dụng viên ngọc này để làm phép thuật của nó.

Đá quý này cũng bao gồm một hệ nhị phân mà bạn có thể sử dụng để phân tích một số mã trực tiếp và xem cây phân tích kết quả.

Đây là một ví dụ :

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

Đầu ra có dạng như sau:

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

Điều này có thể hữu ích nếu bạn đang cố gắng hiểu cách Ruby phân tích một số mã. Nhưng nếu bạn muốn tạo các công cụ phân tích của riêng mình, bạn sẽ phải đọc tệp nguồn, phân tích cú pháp và sau đó duyệt qua cây đã tạo.

Ví dụ :

require 'parser/current'

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

Trình phân tích cú pháp sẽ trả về một AST (Cây cú pháp trừu tượng) cho mã của bạn. Đừng quá sợ hãi bởi cái tên, nó đơn giản hơn nó nghe 🙂

Duyệt qua AST

Bây giờ bạn đã phân tích cú pháp mã của mình bằng trình phân tích cú pháp đá quý bạn cần xem qua AST kết quả.

Bạn có thể thực hiện việc này bằng cách tạo một lớp kế thừa từ AST ::Processor .

Ví dụ :

Bộ xử lý lớp
class Processor < AST::Processor
end

Sau đó, bạn phải khởi tạo lớp này và gọi .process phương pháp:

ast = Processor.new
ast.process(parsed_code)

Bạn cần xác định một số on_ các phương pháp. Các phương pháp này tương ứng với tên nút trong AST.

Để khám phá những phương thức bạn cần xác định, bạn có thể thêm handler_missing vào lớp Bộ xử lý của bạn. Bạn cũng cần on_begin phương pháp.

Bộ xử lý lớp
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í của chúng tôi :

Bạn có Ruby AST và một bộ xử lý cơ bản, khi chạy mã này, bạn sẽ thấy các loại nút cho AST của mình.

Bây giờ :

Bạn cần triển khai tất cả on_ phương pháp mà bạn muốn sử dụng. Ví dụ:nếu tôi muốn tất cả các tên phương thức phiên bản cùng với số dòng của chúng, tôi có thể thực hiện điều này:

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

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

Khi bạn chạy chương trình của mình ngay bây giờ, nó sẽ in ra tất cả các tên phương thức được tìm thấy.

Kết luận

Việc xây dựng một công cụ phân tích tĩnh của Ruby không quá khó. Nếu bạn muốn có một ví dụ đầy đủ hơn, hãy xem gem class_indexer của tôi. Bây giờ đến lượt bạn tự chế tạo các công cụ của riêng mình!

Vui lòng chia sẻ bài đăng này nếu bạn thích nó! 🙂