สมมติว่าคุณต้องการแยกวิเคราะห์ซอร์สโค้ดของคุณเพื่อค้นหาเมธอดทั้งหมดของคุณ มีการกำหนดไว้ที่ไหน และต้องใช้อาร์กิวเมนต์ใด
คุณทำสิ่งนี้ได้อย่างไร
แนวคิดแรกของคุณอาจเป็นการเขียน regexp สำหรับมัน…
แต่มีวิธีที่ดีกว่านี้ไหม
ใช่!
การวิเคราะห์แบบสถิต เป็นเทคนิคที่คุณสามารถใช้เมื่อคุณต้องการดึงข้อมูลจากซอร์สโค้ดเอง
ทำได้โดยการแปลงซอร์สโค้ดเป็นโทเค็น (การแยกวิเคราะห์)
เริ่มกันเลย!
การใช้ Parser Gem
Ruby มี parser อยู่ในไลบรารีมาตรฐาน ชื่อ Ripper เอาต์พุตใช้งานได้ยาก ดังนั้นฉันจึงชอบใช้ parser 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)
parser จะส่งคืน AST (Abstract Syntax Tree) ของรหัสของคุณ อย่ากลัวชื่อมาก มันง่ายกว่าที่คิด 🙂
ข้ามผ่าน AST
ตอนนี้คุณได้แยกวิเคราะห์โค้ดของคุณโดยใช้ parser อัญมณีที่คุณต้องสำรวจ AST ที่ได้
คุณสามารถทำได้โดยการสร้างคลาสที่สืบทอดจาก 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 static นั้นไม่ยากอย่างที่คิด หากคุณต้องการตัวอย่างที่สมบูรณ์กว่านี้ ให้ดูที่ class_indexer gem ของฉัน ถึงเวลาสร้างเครื่องมือของคุณเองแล้ว!
กรุณา แชร์โพสต์นี้ ถ้าคุณสนุกกับมัน! 🙂