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?

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! 🙂