Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

การวิเคราะห์แบบคงที่ใน Ruby

สมมติว่าคุณต้องการแยกวิเคราะห์ซอร์สโค้ดของคุณเพื่อค้นหาเมธอดทั้งหมดของคุณ มีการกำหนดไว้ที่ไหน และต้องใช้อาร์กิวเมนต์ใด

คุณทำสิ่งนี้ได้อย่างไร

แนวคิดแรกของคุณอาจเป็นการเขียน regexp สำหรับมัน…

แต่มีวิธีที่ดีกว่านี้ไหม

การวิเคราะห์แบบคงที่ใน Ruby

ใช่!

การวิเคราะห์แบบสถิต เป็นเทคนิคที่คุณสามารถใช้เมื่อคุณต้องการดึงข้อมูลจากซอร์สโค้ดเอง

ทำได้โดยการแปลงซอร์สโค้ดเป็นโทเค็น (การแยกวิเคราะห์)

เริ่มกันเลย!

การใช้ 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 ของฉัน ถึงเวลาสร้างเครื่องมือของคุณเองแล้ว!

กรุณา แชร์โพสต์นี้ ถ้าคุณสนุกกับมัน! 🙂