কম্পিউটার

রুবি দিয়ে কীভাবে পার্সার তৈরি করবেন

পার্সিং হল একগুচ্ছ স্ট্রিংকে বোঝানো এবং আমরা বুঝতে পারি এমন কিছুতে রূপান্তর করার শিল্প। আপনি রেগুলার এক্সপ্রেশন ব্যবহার করতে পারেন, কিন্তু সেগুলো সবসময় কাজের জন্য উপযুক্ত হয় না।

উদাহরণস্বরূপ, এটা সাধারণ জ্ঞান যে রেগুলার এক্সপ্রেশন সহ HTML পার্স করা সম্ভবত একটি ভাল ধারণা নয়।

রুবিতে আমাদের নোকোগিরি রয়েছে যা আমাদের জন্য এই কাজটি করতে পারে, তবে আপনি নিজের পার্সার তৈরি করে অনেক কিছু শিখতে পারেন। চলুন শুরু করা যাক!

রুবির সাথে পার্সিং

আমাদের পার্সারের মূল হল স্ট্রিংস্ক্যানার ক্লাস।

এই ক্লাসে একটি স্ট্রিং এবং একটি অবস্থান পয়েন্টারের একটি অনুলিপি রয়েছে। পয়েন্টার আমাদের নির্দিষ্ট টোকেনের সন্ধানে স্ট্রিংটি অতিক্রম করার অনুমতি দেবে।

আমরা যে পদ্ধতিগুলি ব্যবহার করব তা হল:

  • .পিক
  • .স্ক্যান_এখন পর্যন্ত
  • .পান

আরেকটি দরকারী পদ্ধতি হল .স্ক্যান (এখন পর্যন্ত ছাড়া)।

দ্রষ্টব্য :

স্ট্রিংস্ক্যানার আপনার কাছে উপলব্ধ না হলে require 'strscan' যোগ করার চেষ্টা করুন

আমি ডকুমেন্টেশন হিসাবে দুটি পরীক্ষা লিখেছি যাতে আমরা বুঝতে পারি যে এই ক্লাসটি কীভাবে কাজ করবে:

describe StringScanner do
  let (:buff) { StringScanner.new "testing" }

  it "can peek one step ahead" do
    expect(buff.peek 1).to eq "t"
  end

  it "can read one char and return it" do
    expect(buff.getch).to eq "t"
    expect(buff.getch).to eq "e"
  end
end

এই ক্লাস সম্পর্কে একটি গুরুত্বপূর্ণ বিষয় লক্ষ্য করা যায় যে কিছু পদ্ধতি পজিশন পয়েন্টারকে অগ্রসর করে (গেট, স্ক্যান ), অন্যরা করে না (উঁকি ) যেকোনো সময়ে আপনি আপনার স্ক্যানার পরিদর্শন করতে পারেন (.inspect ব্যবহার করে অথবা p এটি কোথায় আছে তা দেখতে৷

পার্সার ক্লাস

পার্সার ক্লাস হল যেখানে বেশিরভাগ কাজ হয়, আমরা এটিকে টেক্সটের স্নিপেট দিয়ে শুরু করব যা আমরা পার্স করতে চাই এবং এটি তার জন্য একটি স্ট্রিংস্ক্যানার তৈরি করবে এবং পার্স পদ্ধতিতে কল করবে:

def initialize(str)
  @buffer = StringScanner.new(str)
  @tags   = []
  parse
end

পরীক্ষায় আমরা এটিকে এভাবে সংজ্ঞায়িত করি:

let(:parser) { Parser.new "<body>testing</body> <title>parsing with ruby</title>" }

এই ক্লাসটি কীভাবে কাজ করে তা আমরা একটু পরে দেখব, তবে প্রথমে আমাদের প্রোগ্রামের শেষ অংশটি একবার দেখে নেওয়া যাক।

ট্যাগ ক্লাস

এই ক্লাসটি খুবই সহজ, এটি মূলত পার্সিং ফলাফলের জন্য একটি ধারক এবং ডেটা ক্লাস হিসেবে কাজ করে।

class Tag
  attr_reader :name
  attr_accessor :content

  def initialize(name)
    @name = name
  end
end

পার্স করা যাক!

কিছু পার্স করার জন্য প্যাটার্ন খুঁজে পেতে আমাদের ইনপুট টেক্সট দেখতে হবে। উদাহরণস্বরূপ, আমরা জানি HTML কোডের নিম্নলিখিত ফর্ম রয়েছে:

<tag>contents</tag>

স্পষ্টতই দুটি ভিন্ন উপাদান রয়েছে যা আমরা এখানে সনাক্ত করতে পারি, ট্যাগের নাম এবং ট্যাগের ভিতরের পাঠ্য। আমরা যদি BNF স্বরলিপি ব্যবহার করে একটি আনুষ্ঠানিক ব্যাকরণ সংজ্ঞায়িত করি তবে এটি দেখতে এরকম কিছু হবে:

tag = <opening_tag> <contents> <closing_tag>
opening_tag = "<" <tag_name> ">"
closing_tag = "</" <tag_name> ">"

আমরা StringScanners এর পিক ব্যবহার করতে যাচ্ছি আমাদের ইনপুট বাফারের পরবর্তী চিহ্নটি একটি খোলার ট্যাগ কিনা তা দেখতে। যদি তা হয় তাহলে আমরা ফাইন্ড_ট্যাগকে কল করব এবং find_content আমাদের পার্সার ক্লাসে পদ্ধতি:

def parse_element
  if @buffer.peek(1) == '<'
    @tags << find_tag
    last_tag.content = find_content
  end
end

ফাইন্ড_ট্যাগ পদ্ধতি হবে:

  • 'Consume' ওপেনিং ট্যাগ ক্যারেক্টার
  • ক্লোজিং চিহ্ন (“>”) না পাওয়া পর্যন্ত স্ক্যান করুন
  • ট্যাগ নামের একটি নতুন ট্যাগ অবজেক্ট তৈরি করুন এবং ফেরত দিন

এখানে কোড আছে, লক্ষ্য করুন কিভাবে আমাদের কাপ করতে হবে শেষ চরিত্র। এর কারণ স্ক্যান_অন্তত ফলাফলে '>' অন্তর্ভুক্ত করে, এবং আমরা তা চাই না।

def find_tag
  @buffer.getch
  tag = @buffer.scan_until />/
  Tag.new(tag.chop)
end

পরবর্তী ধাপ হল ট্যাগের ভেতরের বিষয়বস্তু খুঁজে বের করা, এটি খুব বেশি কঠিন হওয়া উচিত নয় কারণ scan_until পদ্ধতি পজিশন পয়েন্টারকে সঠিক জায়গায় নিয়ে যায়। আমরা ক্লোজিং ট্যাগ খুঁজতে এবং ট্যাগের বিষয়বস্তু ফেরত দিতে আবার scan_until ব্যবহার করতে যাচ্ছি।

রুবি দিয়ে কীভাবে পার্সার তৈরি করবেন

def find_content
  tag = last_tag.name
  content = @buffer.scan_until /<\/#{tag}>/
  content.sub("</#{tag}>", "")
end

এখন :

আমাদের যা করতে হবে তা হল parse_element একটি লুপে যতক্ষণ না আমরা আমাদের ইনপুট বাফারে আরও ট্যাগ খুঁজে পাই না৷

৷ @buffer.eos পর্যন্ত
def parse
  until @buffer.eos?
    skip_spaces
    parse_element
  end
end

আপনি এখানে সম্পূর্ণ কোডটি খুঁজে পেতে পারেন: https://github.com/matugm/simple-parser। আপনি বর্ধিত সংস্করণের জন্য 'নেস্টেড_ট্যাগস' শাখাটিও দেখতে পারেন যা অন্য ট্যাগের ভিতরে ট্যাগগুলির সাথে ডিল করতে পারে৷

উপসংহার

একটি পার্সার লেখা একটি আকর্ষণীয় বিষয় এবং এটি মাঝে মাঝে বেশ জটিলও হতে পারে৷

আপনি যদি স্ক্র্যাচ থেকে নিজের পার্সার তৈরি করতে না চান তবে আপনি তথাকথিত 'পার্সার জেনারেটর'গুলির একটি ব্যবহার করতে পারেন। রুবিতে আমাদের ট্রিটপ এবং পার্সলেট আছে।


  1. রুবি গ্রেপ পদ্ধতি কীভাবে ব্যবহার করবেন (উদাহরণ সহ)

  2. রুবি মানচিত্র পদ্ধতি কীভাবে ব্যবহার করবেন (উদাহরণ সহ)

  3. রুবিতে ফাইলগুলি কীভাবে পড়তে এবং লিখতে হয় (উদাহরণ সহ)

  4. SwiftUI এর সাথে কীভাবে ডিজাইন সিস্টেম তৈরি করবেন