কম্পিউটার

রুবি কিভাবে আপনার প্রোগ্রাম ব্যাখ্যা করে এবং চালায়

আপনি আপনার সরঞ্জামগুলি সম্পর্কে যত বেশি জানবেন, একজন বিকাশকারী হিসাবে আপনি তত ভাল সিদ্ধান্ত নেবেন। এটি প্রায়শই দরকারী - বিশেষ করে যখন পারফরম্যান্স সমস্যাগুলি ডিবাগ করার সময় - আপনার প্রোগ্রামটি চালানোর সময় রুবি আসলে কী করছে তা বোঝার জন্য৷

এই পোস্টে আমরা একটি সাধারণ প্রোগ্রামের যাত্রা অনুসরণ করব কারণ এটি লেক্সড, পার্স করা এবং বাইটকোডে কম্পাইল করা হয়েছে। রুবি আমাদের যে টুলগুলি দেয় তা আমরা প্রতিটি পদক্ষেপে দোভাষীর উপর গোয়েন্দাগিরি করার জন্য ব্যবহার করব।

চিন্তা করবেন না — আপনি একজন বিশেষজ্ঞ না হলেও এই পোস্টটি অনুসরণ করা বেশ সহজ হওয়া উচিত। এটি একটি টেকনিক্যাল ম্যানুয়ালের চেয়ে একটি নির্দেশিত সফর।

আমাদের নমুনা প্রোগ্রামের সাথে দেখা করুন

একটি উদাহরণ হিসাবে, আমি একটি একক if/else বিবৃতি ব্যবহার করতে যাচ্ছি। স্থান বাঁচাতে, আমি টারনারি অপারেটর ব্যবহার করে এটি লিখব। কিন্তু প্রতারিত হবেন না, এটি শুধুমাত্র একটি যদি/অন্যথা।

x > 100 ? 'foo' : 'bar'

যেমন আপনি দেখতে পাবেন, এমনকি এই ধরনের একটি সাধারণ প্রোগ্রাম প্রক্রিয়াকরণের সাথে সাথে অনেক ডেটাতে অনুবাদ করা হয়।

দ্রষ্টব্য:এই পোস্টের সমস্ত উদাহরণ রুবি (MRI) 2.2-এ লেখা হয়েছে। আপনি যদি রুবির অন্যান্য বাস্তবায়ন ব্যবহার করেন তবে তারা সম্ভবত কাজ করবে না।

টোকেনাইজিং

রুবি ইন্টারপ্রেটার আপনার প্রোগ্রামটি চালানোর আগে এটিকে কিছুটা ফ্রি-ফর্ম প্রোগ্রামিং ভাষা থেকে আরও কাঠামোগত ডেটাতে রূপান্তর করতে হবে।

প্রথম পদক্ষেপটি হতে পারে প্রোগ্রামটিকে টুকরো টুকরো করা। এই অংশগুলিকে টোকেন বলা হয়।

# This is a string
"x > 1"

# These are tokens
["x", ">", "1"]

রুবি স্ট্যান্ডার্ড লাইব্রেরি রিপার নামে একটি মডিউল সরবরাহ করে যা আমাদের রুবি দোভাষীর মতো একইভাবে রুবি কোড প্রক্রিয়া করতে দেয়।

নীচের উদাহরণে আমরা আমাদের রুবি কোডে টোকেনাইজ পদ্ধতি ব্যবহার করছি। আপনি দেখতে পাচ্ছেন, এটি টোকেনের একটি অ্যারে প্রদান করে।

require 'ripper'
Ripper.tokenize("x > 1 ? 'foo' : 'bar'")
# => ["x", " ", ">", " ", "1", " ", "?", " ", "'", "foo", "'", " ", ":", " ", "'", "bar", "'"]

টোকেনাইজার বেশ বোকা। আপনি এটি সম্পূর্ণরূপে অবৈধ রুবি খাওয়াতে পারেন এবং এটি এখনও এটিকে টোকেনাইজ করবে।

# bad code
Ripper.tokenize("1var @= \/foobar`")
# => ["1", "var"]

লেক্সিং

লেক্সিং টোকেনাইজেশনের বাইরে এক ধাপ। স্ট্রিংটি এখনও টোকেনগুলিতে বিভক্ত, তবে টোকেনগুলিতে অতিরিক্ত ডেটা যোগ করা হয়েছে৷

নীচের উদাহরণে আমরা আমাদের ছোট প্রোগ্রাম লেক্সে রিপার ব্যবহার করছি। আপনি দেখতে পাচ্ছেন, এটি এখন প্রতিটি টোকেনকে একটি শনাক্তকারী হিসাবে ট্যাগ করছে :on_ident , একজন অপারেটর :on_op , একটি পূর্ণসংখ্যা :on_int , ইত্যাদি।

require 'ripper'
require 'pp'

pp Ripper.lex("x > 100 ? 'foo' : 'bar'")

# [[[1, 0], :on_ident, "x"],
#  [[1, 1], :on_sp, " "],
#  [[1, 2], :on_op, ">"],
#  [[1, 3], :on_sp, " "],
#  [[1, 4], :on_int, "100"],
#  [[1, 5], :on_sp, " "],
#  [[1, 6], :on_op, "?"],
#  [[1, 7], :on_sp, " "],
#  [[1, 8], :on_tstring_beg, "'"],
#  [[1, 9], :on_tstring_content, "foo"],
#  [[1, 12], :on_tstring_end, "'"],
#  [[1, 13], :on_sp, " "],
#  [[1, 14], :on_op, ":"],
#  [[1, 15], :on_sp, " "],
#  [[1, 16], :on_tstring_beg, "'"],
#  [[1, 17], :on_tstring_content, "bar"],
#  [[1, 20], :on_tstring_end, "'"]]

এই মুহুর্তে এখনও কোনও বাস্তব সিনট্যাক্স পরীক্ষা চলছে না। লেক্সার আনন্দের সাথে অবৈধ কোড প্রক্রিয়া করবে।

পার্সিং

এখন যেহেতু রুবি কোডটিকে আরও পরিচালনাযোগ্য অংশে বিভক্ত করেছে, এটি পার্সিং শুরু করার সময়।

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

আপনি বলতে পারেন যে সাধারণভাবে প্রোগ্রামিং ভাষাগুলি বিমূর্ত সিনট্যাক্স ট্রি বর্ণনা করার আরও বেশি ব্যবহারকারী-বান্ধব উপায়৷

require 'ripper'
require 'pp'

pp Ripper.sexp("x > 100 ? 'foo' : 'bar'")

# [:program,
#  [[:ifop,
#    [:binary, [:vcall, [:@ident, "x", [1, 0]]], :>, [:@int, "100", [1, 4]]],
#    [:string_literal, [:string_content, [:@tstring_content, "foo", [1, 11]]]],
#    [:string_literal, [:string_content, [:@tstring_content, "foobar", [1, 19]]]]]]]

এই আউটপুটটি পড়া সহজ নাও হতে পারে, কিন্তু আপনি যদি এটির দিকে দীর্ঘক্ষণ তাকিয়ে থাকেন তবে আপনি দেখতে পাবেন কিভাবে এটি মূল প্রোগ্রামে ম্যাপ করে৷

# Define a progam
[:program,
 # Do an "if" operation
 [[:ifop,
   # Check the conditional (x > 100)
   [:binary, [:vcall, [:@ident, "x", [1, 0]]], :>, [:@int, "100", [1, 4]]],
   # If true, return "foo"
   [:string_literal, [:string_content, [:@tstring_content, "foo", [1, 11]]]],
   # If false, return "bar"
   [:string_literal, [:string_content, [:@tstring_content, "foobar", [1, 19]]]]]]]

এই মুহুর্তে, রুবি দোভাষী জানেন যে আপনি এটি কি করতে চান। এটা এখন আপনার প্রোগ্রাম চালাতে পারে. এবং রুবি 1.9 এর আগে, এটি হবে। কিন্তু এখন, আরো একটি ধাপ আছে.

বাইটকোডে কম্পাইল করা হচ্ছে

বিমূর্ত সিনট্যাক্স ট্রিকে সরাসরি ট্র্যাভার্স করার পরিবর্তে, আজকাল রুবি বিমূর্ত সিনট্যাক্স ট্রিকে নিম্ন-স্তরের বাইট কোডে কম্পাইল করে।

এই বাইট কোডটি তখন রুবি ভার্চুয়াল মেশিন দ্বারা চালিত হয়।

আমরা RubyVM::InstructionSequence এর মাধ্যমে ভার্চুয়াল মেশিনের ভিতরের কাজগুলি দেখতে পারি ক্লাস নীচের উদাহরণে, আমরা আমাদের নমুনা প্রোগ্রাম কম্পাইল করি এবং তারপর এটিকে বিচ্ছিন্ন করি যাতে একজন মানুষের পাঠযোগ্য হয়৷

puts RubyVM::InstructionSequence.compile("x > 100 ? 'foo' : 'bar'").disassemble
# == disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
# 0000 trace            1                                               (   1)
# 0002 putself
# 0003 opt_send_without_block <callinfo!mid:x, argc:0, FCALL|VCALL|ARGS_SIMPLE>
# 0005 putobject        100
# 0007 opt_gt           <callinfo!mid:>, argc:1, ARGS_SIMPLE>
# 0009 branchunless     15
# 0011 putstring        "foo"
# 0013 leave
# 0014 pop
# 0015 putstring        "bar"
# 0017 leave

ছিঃ! এটি হঠাৎ রুবির চেয়ে অ্যাসেম্বলি ভাষার মতো দেখায়। আসুন এটির মধ্য দিয়ে ধাপে ধাপে দেখি এবং আমরা এটির অর্থ করতে পারি কিনা।

# Call the method `x` on self and save the result on the stack
0002 putself
0003 opt_send_without_block <callinfo!mid:x, argc:0, FCALL|VCALL|ARGS_SIMPLE>

# Put the number 100 on the stack
0005 putobject        100

# Do the comparison (x > 100)
0007 opt_gt           <callinfo!mid:>, argc:1, ARGS_SIMPLE>

# If the comparison was false, go to line 15
0009 branchunless     15

# If the comparison was true, return "foo"
0011 putstring        "foo"
0013 leave
0014 pop

# Here's line 15. We jumped here if comparison was false. Return "bar"
0015 putstring        "bar"
0017 leave

রুবি ভার্চুয়াল মেশিন (YARV) তারপরে এই নির্দেশাবলীর মাধ্যমে পদক্ষেপ করে এবং সেগুলি কার্যকর করে। এটাই!

উপসংহার

এটি রুবি দোভাষীর আমাদের খুব সরলীকৃত, কার্টুনি সফরের সমাপ্তি ঘটায়। আমি আপনাকে এখানে যে সরঞ্জামগুলি দেখিয়েছি তার সাহায্যে, রুবি কীভাবে আপনার প্রোগ্রামগুলিকে ব্যাখ্যা করছে তা থেকে অনেক অনুমান করা সম্ভব। আমি বলতে চাচ্ছি, এটি একটি AST এর চেয়ে বেশি কংক্রিট পায় না। এবং পরের বার আপনি কিছু অদ্ভুত পারফরম্যান্স সমস্যা দ্বারা স্তব্ধ হয়ে গেলে, বাইটকোড দেখার চেষ্টা করুন। এটি সম্ভবত আপনার সমস্যার সমাধান করবে না, তবে এটি আপনার মনকে সরিয়ে নিতে পারে। :)


  1. আপনার রুবি পদ্ধতি গুপ্তচর কিভাবে

  2. কীভাবে আপনার রুবি প্রোগ্রামগুলি ডিবাগ এবং ঠিক করবেন

  3. কীভাবে একটি কীলগার সনাক্ত এবং সরান

  4. কিভাবে আপনার পিসিতে অবাঞ্ছিত প্রোগ্রাম আনইনস্টল করবেন