কম্পিউটার

রুবিতে একটি নতুন প্রোগ্রামিং ভাষা তৈরি করা:দোভাষী

গিথুবের সম্পূর্ণ উৎস

Stoffle প্রোগ্রামিং ভাষার একটি সম্পূর্ণ বাস্তবায়ন GitHub এ উপলব্ধ। আপনি বাগ খুঁজে পেলে বা প্রশ্ন থাকলে নির্দ্বিধায় একটি সমস্যা খুলুন৷

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

আমরা যে দোভাষী তৈরি করতে যাচ্ছি তাকে সাধারণত ট্রি-ওয়াক ইন্টারপ্রেটার বলা হয়। এই সিরিজের আগের পোস্টে, আমরা টোকেনের সমতল ক্রমকে ট্রি ডেটা স্ট্রাকচারে রূপান্তর করতে একটি পার্সার তৈরি করেছি (একটি বিমূর্ত সিনট্যাক্স ট্রি, বা সংক্ষেপে AST)। আপনি হয়তো কল্পনা করছেন, আমাদের দোভাষীর কাজ আমাদের পার্সার দ্বারা তৈরি AST এর মধ্য দিয়ে যাওয়া এবং একটি Stoffle প্রোগ্রামে জীবন শ্বাস নেওয়া। আমি এই ভাষা বাস্তবায়ন যাত্রার মধ্যে এই শেষ ধাপটিকে সবচেয়ে উত্তেজনাপূর্ণ বলে মনে করি। দোভাষী তৈরি করার সময়, সবকিছুই শেষ পর্যন্ত ক্লিক করে এবং আমরা স্টফল প্রোগ্রামগুলি বাস্তবে চলমান দেখতে সক্ষম!

আমি দুটি অংশে দোভাষীর বাস্তবায়ন দেখাতে এবং ব্যাখ্যা করতে যাচ্ছি। এই প্রথম অংশে, আমরা বেসিক কাজ করতে যাচ্ছি:ভেরিয়েবল, কন্ডিশনাল, ইউনারি এবং বাইনারি অপারেটর, ডেটা টাইপ এবং কনসোলে মুদ্রণ। আমরা আমাদের দোভাষী বাস্তবায়নের জন্য দ্বিতীয় এবং শেষ পোস্টের জন্য আরও মাংসযুক্ত জিনিস (ফাংশন সংজ্ঞা, ফাংশন কলিং, লুপ, ইত্যাদি) সংরক্ষণ করছি৷

লেক্সার এবং পার্সারের একটি দ্রুত সংকলন

আমরা ডুব দিয়ে দোভাষী প্রয়োগ করা শুরু করার আগে, আসুন আমরা এই সিরিজের আগের পোস্টগুলিতে কী করেছি তা দ্রুত মনে করিয়ে দেই। প্রথমত, আমরা লেক্সার তৈরি করেছি, যা কাঁচা সোর্স কোডকে টোকেনে রূপান্তরিত করে। তারপরে, আমরা পার্সার প্রয়োগ করেছি, টোকেনগুলিকে একটি গাছের কাঠামোতে (AST) রূপ দেওয়ার জন্য দায়ী উপাদান। সংক্ষেপে, আমরা এখন পর্যন্ত যে রূপান্তরগুলি পর্যবেক্ষণ করেছি তা এখানে:

স্থিতি 0:উৎস

my_var = 1

স্টেট 1:লেক্সার কাঁচা সোর্স কোডকে টোকেনে রূপান্তরিত করে

[:identifier, :'=', :number]

স্টেট 2:পার্সার টোকেনগুলিকে একটি বিমূর্ত সিনট্যাক্স ট্রিতে রূপান্তরিত করে

রুবিতে একটি নতুন প্রোগ্রামিং ভাষা তৈরি করা:দোভাষী

এটা সবই হাঁটার বিষয়ে

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

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

num = -2
if num > 0
  println("The number is greater than zero.")
else
  println("The number is less than or equal to zero.")
end

এটি একই প্রোগ্রামের জন্য উত্পাদিত AST:

রুবিতে একটি নতুন প্রোগ্রামিং ভাষা তৈরি করা:দোভাষী

আমাদের হাঁটার প্রথম ধাপ

আপনি সম্ভবত এই সিরিজের শেষ পোস্ট থেকে মনে রাখবেন, একটি Stoffle AST সর্বদা তার মূল হিসাবে থাকে একটি AST::Program নোড এই মূলে সাধারণত একাধিক সন্তান থাকে। তাদের মধ্যে কিছু অগভীর হবে (একটি সাধারণ পরিবর্তনশীল অ্যাসাইনমেন্টের জন্য উত্পাদিত AST সম্পর্কে চিন্তা করুন)। অন্যান্য শিশুরা বেশ গভীর উপবৃক্ষের মূল হতে পারে (একটি লুপের কথা চিন্তা করুন যার শরীরের ভিতরে অনেকগুলি লাইন রয়েছে)। এই রুবি কোডটি আমাদের AST এর মধ্য দিয়ে হাঁটা শুরু করতে হবে যা আমাদের দোভাষীর কাছে দেওয়া হয়েছিল:

module Stoffle
  class Interpreter
    attr_reader :program, :output, :env

    def initialize
      @output = []
      @env = {}
    end

    def interpret(ast)
      @program = ast

      interpret_nodes(program.expressions)
    end

    private

    def interpret_nodes(nodes)
      last_value = nil

      nodes.each do |node|
        last_value = interpret_node(node)
      end

      last_value
    end

    def interpret_node(node)
      interpreter_method = "interpret_#{node.type}"
      send(interpreter_method, node)
    end

    #...

  end
end

যখন একটি নতুন Interpreter ইনস্ট্যান্সিয়েটেড করা হয়, শুরু থেকেই আমরা দুটি ইনস্ট্যান্স ভেরিয়েবল তৈরি করি:@output এবং @env . প্রাক্তনের দায়িত্ব হল কালানুক্রমিক ক্রমানুসারে, আমাদের প্রোগ্রাম যা মুদ্রিত করেছে তা সংরক্ষণ করা। স্বয়ংক্রিয় পরীক্ষা বা ডিবাগিং লেখার সময় এই তথ্যটি হাতে থাকা খুবই কার্যকর। @env এর দায়িত্ব একটু ভিন্ন। আমরা "পরিবেশ" এর রেফারেন্স হিসাবে এটির নাম দিয়েছি। নামটি সুপারিশ করতে পারে, এর কাজটি আমাদের চলমান প্রোগ্রামের অবস্থা ধরে রাখা। এর একটি কাজ হবে একটি শনাক্তকারীর (যেমন, একটি পরিবর্তনশীল নাম) এবং এর বর্তমান মানের মধ্যে বাঁধাই বাস্তবায়ন করা।

#interpret_nodes মেথড রুট নোডের (AST::Program) সমস্ত সন্তানের মাধ্যমে লুপ করে ) তারপর, এটি #interpret_node কল করে প্রতিটি পৃথক নোডের জন্য।

#interpret_node সহজ কিন্তু তবুও আকর্ষণীয়। এখানে, আমরা বর্তমানে হাতে থাকা নোড টাইপ পরিচালনা করার জন্য উপযুক্ত পদ্ধতি কল করতে রুবি মেটাপ্রোগ্রামিং ব্যবহার করি। উদাহরণস্বরূপ, একটি AST::VarBinding এর জন্য নোড, #interpret_var_binding পদ্ধতি বলা হয় যে এক হয়.

অবশ্যই, আমাদের ভেরিয়েবল সম্পর্কে কথা বলতে হবে

আমরা যে উদাহরণ প্রোগ্রামের মধ্য দিয়ে যাচ্ছি তার AST-এ প্রথম যে নোডটি ব্যাখ্যা করতে হবে তা হল একটি AST::VarBinding . এটি @left একটি AST::Identifier , এবং এর @right একটি AST::UnaryOperator . চলুন একটি পরিবর্তনশীল বাইন্ডিং ব্যাখ্যা করার জন্য দায়ী পদ্ধতিটি একবার দেখে নেওয়া যাক:

def interpret_var_binding(var_binding)
  env[var_binding.var_name_as_str] = interpret_node(var_binding.right)
end

আপনি দেখতে পারেন, এটা বেশ সোজা. আমরা @env-এ একটি কী-মানের জোড়া যোগ করি (বা ওভাররাইট করি) হ্যাশ।

কী হল ভেরিয়েবলের নাম (#var_name_as_str var_binding.left.name এর সমতুল্য একটি সহায়ক পদ্ধতি ) এই মুহুর্তে, সমস্ত ভেরিয়েবল বিশ্বব্যাপী। আমরা পরবর্তী পোস্টে স্কোপিং পরিচালনা করব।

মানটি অ্যাসাইনমেন্টের ডানদিকের অভিব্যক্তিটির ব্যাখ্যা করার ফলাফল। এটি করতে, আমরা #interpret_node ব্যবহার করি আবার যেহেতু আমাদের একটি AST::UnaryOperator আছে ডানদিকে, পরবর্তী পদ্ধতি যাকে বলা হয় তা হল #interpret_unary_operator :

def interpret_unary_operator(unary_op)
  case unary_op.operator
  when :'-'
    -(interpret_node(unary_op.operand))
  else # :'!'
    !(interpret_node(unary_op.operand))
  end
end

স্টফলের সমর্থিত ইউনারী অপারেটরগুলির শব্দার্থবিদ্যা (- এবং ! ) রুবি হিসাবে একই. ফলস্বরূপ, বাস্তবায়ন সহজ হতে পারে না:আমরা রুবির - প্রয়োগ করি অপারেন্ড ব্যাখ্যা করার ফলাফলে অপারেটর। স্বাভাবিক সন্দেহভাজন, #interpret_node , এখানে আবার দেখা যাচ্ছে। আপনি আমাদের প্রোগ্রামের AST থেকে মনে রাখতে পারেন, --এর অপারেন্ড একটি AST::Number (সংখ্যা 2 ) এর মানে আমাদের পরবর্তী স্টপ #interpret_number-এ :

def interpret_number(number)
  number.value
end

#interpret_number এর বাস্তবায়ন কেক একটি টুকরা হয়. সংখ্যার আক্ষরিক প্রতিনিধিত্ব হিসাবে একটি রুবি ফ্লোট গ্রহণ করার আমাদের সিদ্ধান্ত (এটি লেক্সারে ঘটে!) এখানে প্রদান করে। @value AST::Number এর নোড ইতিমধ্যেই সংখ্যার আমাদের পছন্দসই অভ্যন্তরীণ উপস্থাপনা ধারণ করে, তাই আমরা এটি পুনরুদ্ধার করি৷

এর সাথে, আমরা AST::Program-এর প্রথম সরাসরি সন্তানের ব্যাখ্যা শেষ করি . এখন, আমাদের প্রোগ্রামের ব্যাখ্যা শেষ করার জন্য, আমাদের অবশ্যই এর অন্য, আরও লোমশ, শিশুকে পরিচালনা করতে হবে:AST::Conditional টাইপের একটি নোড .

নিয়ম ও শর্তাবলী প্রযোজ্য হতে পারে

#interpret_nodes-এ ফিরে যান , আমাদের সেরা বন্ধু #interpret_node AST::Program-এর পরবর্তী সরাসরি সন্তানকে ব্যাখ্যা করতে আবার ডাকা হয় .

def interpret_nodes(nodes)
  last_value = nil

  nodes.each do |node|
    last_value = interpret_node(node)
  end

  last_value
end

একটি AST::Conditional ব্যাখ্যা করার জন্য দায়ী পদ্ধতি #interpret_conditional . যাইহোক, এটি একবার দেখে নেওয়ার আগে, আসুন AST::Conditional এর বাস্তবায়ন পর্যালোচনা করে আমাদের স্মৃতিগুলিকে রিফ্রেশ করি নিজেই:

class Stoffle::AST::Conditional < Stoffle::AST::Expression
  attr_accessor :condition, :when_true, :when_false

  def initialize(cond_expr = nil, true_block = nil, false_block = nil)
    @condition = cond_expr
    @when_true = true_block
    @when_false = false_block
  end

  def ==(other)
    children == other&.children
  end

  def children
    [condition, when_true, when_false]
  end
end

ঠিক আছে, তাই @condition একটি অভিব্যক্তি ধারণ করে যা সত্য বা মিথ্যা হবে; @when_true @condition এর ক্ষেত্রে এক বা একাধিক এক্সপ্রেশন সহ একটি ব্লক ধরে রাখে এটি সত্য, এবং @when_false (ELSE clause) @condition এর ক্ষেত্রে ব্লকটি চালানো হবে মিথ্যা হতে হবে।

এখন, #interpret_condition দেখে নেওয়া যাক :

def interpret_conditional(conditional)
  evaluated_cond = interpret_node(conditional.condition)

  # We could implement the line below in a shorter way, but better to be explicit about truthiness in Stoffle.
  if evaluated_cond == nil || evaluated_cond == false
    return nil if conditional.when_false.nil?

    interpret_nodes(conditional.when_false.expressions)
  else
    interpret_nodes(conditional.when_true.expressions)
  end
end

স্টফলে সত্যতা রুবির মতোই। অন্য কথায়, স্টফলে, শুধুমাত্র nil এবং false মিথ্যা হয় শর্তে অন্য যেকোনো ইনপুট সত্য।

আমরা প্রথমে conditional.condition দ্বারা ধারণ করা অভিব্যক্তিটিকে ব্যাখ্যা করে শর্তটি মূল্যায়ন করি . আমরা কোন নোডের সাথে কাজ করছি তা বের করতে আমাদের প্রোগ্রামের AST আবার একবার দেখে নেওয়া যাক:

রুবিতে একটি নতুন প্রোগ্রামিং ভাষা তৈরি করা:দোভাষী

দেখা যাচ্ছে যে আমাদের একটি AST::BinaryOperator আছে (> num > 0-এ ব্যবহৃত ) ঠিক আছে, এটি আবার একই পথ:প্রথম #interpret_node , যা #interpret_binary_operator কল করে এই সময়:

def interpret_binary_operator(binary_op)
  case binary_op.operator
  when :and
    interpret_node(binary_op.left) && interpret_node(binary_op.right)
  when :or
    interpret_node(binary_op.left) || interpret_node(binary_op.right)
  else
    interpret_node(binary_op.left).send(binary_op.operator, interpret_node(binary_op.right))
  end
end

আমাদের লজিক্যাল অপারেটর (and এবং or )কে বাইনারি অপারেটর হিসাবে বিবেচনা করা যেতে পারে, তাই আমরা সেগুলিকে এখানেও পরিচালনা করি। যেহেতু তাদের শব্দার্থক রুবির && এর সমতুল্য এবং || , বাস্তবায়ন হল প্লেইন সেলিং, আপনি উপরে দেখতে পাচ্ছেন।

পরবর্তী পদ্ধতির বিভাগটি আমরা সবচেয়ে আগ্রহী; এই বিভাগটি অন্য সব বাইনারি অপারেটর পরিচালনা করে (> সহ ) এখানে, আমরা আমাদের পক্ষে রুবির গতিশীলতা লাভ করতে পারি এবং একটি খুব সংক্ষিপ্ত সমাধান নিয়ে আসতে পারি। রুবিতে, বাইনারি অপারেটরগুলি একটি অপারেশনে অংশগ্রহণকারী বস্তুর পদ্ধতি হিসাবে উপলব্ধ:

-2 > 0           # is equivalent to
-2.send(:'>', 0) # this
# and the following line would be a general solution,
# very similar to what we have in the interpreter
operand_1.send(binary_operator, operand_2)

বাইনারি অপারেটরদের একটি ভার্বোস বাস্তবায়ন

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

আপনি যদি কখনও নিজেকে ভাষা ডিজাইনার/বাস্তবায়ক হিসাবে এমন একটি অবস্থানে পান, আপনি সর্বদা একটি সহজ (কিন্তু সেই মার্জিত নয়) সমাধানে ফিরে যেতে পারেন:একটি সুইচ নির্মাণ ব্যবহার করে। আমাদের ক্ষেত্রে, বাস্তবায়নটি দেখতে কিছুটা এরকম হবে:

# ... inside #interpret_binary_operator ...

case binary_op.operator
when :'+'
  interpret_node(binary_op.left) + interpret_node(binary_op.right)
# ... other operators
end

#interpret_conditional-এ ফিরে যাওয়ার আগে , আসুন কিছু উপেক্ষা করা হয় না তা নিশ্চিত করতে একটি দ্রুত চক্কর নেওয়া যাক। আমরা যে প্রোগ্রামটি ব্যাখ্যা করছি তা যদি আপনার মনে থাকে, তাহলে num ভেরিয়েবল তুলনাতে ব্যবহার করা হয় (বাইনারী অপারেটর > ব্যবহার করে ) আমরা শুধু একসাথে অন্বেষণ করেছি। কিভাবে আমরা বাম অপারেন্ড পুনরুদ্ধার করেছি (অর্থাৎ, num-এ সংরক্ষিত মান পরিবর্তনশীল) যে তুলনা? এর জন্য দায়ী পদ্ধতি হল #interpret_identifier , এবং এর বাস্তবায়ন সহজ-শান্তির:

def interpret_identifier(identifier)
  if env.has_key?(identifier.name)
    env[identifier.name]
  else
    # Undefined variable.
    raise Stoffle::Error::Runtime::UndefinedVariable.new(identifier.name)
  end
end

এখন, #interpret_conditional-এ ফিরে যান . আমাদের ছোট প্রোগ্রামের ক্ষেত্রে, শর্তটি রুবি false এ মূল্যায়ন করা হয়েছে মান আমরা শর্তসাপেক্ষ কাঠামোর IF বা ELSE শাখা চালাতে হবে কিনা তা নির্ধারণ করতে এই মানটি ব্যবহার করি। আমরা ELSE শাখার ব্যাখ্যা করতে এগিয়ে যাই, যার সংশ্লিষ্ট ব্লক কোড conditional.when_false-এ সংরক্ষিত থাকে। . এখানে, আমাদের একটি AST::Block আছে , যা আমাদের AST (AST::Program-এর রুট নোডের সাথে খুব মিল। ) ব্লক, একইভাবে, সম্ভাব্য অভিব্যক্তি একটি গুচ্ছ আছে যে ব্যাখ্যা করা প্রয়োজন. এই উদ্দেশ্যে, আমরা #interpret_nodesও ব্যবহার করি .

def interpret_conditional(conditional)
  evaluated_cond = interpret_node(conditional.condition)

  # We could implement the line below in a shorter way, but better to be explicit about truthiness in Stoffle.
  if evaluated_cond == nil || evaluated_cond == false
    return nil if conditional.when_false.nil?

    interpret_nodes(conditional.when_false.expressions)
  else
    interpret_nodes(conditional.when_true.expressions)
  end
end

পরবর্তী AST নোডটি আমাদের পরিচালনা করতে হবে একটি AST::FunctionCall . একটি ফাংশন কল ব্যাখ্যা করার জন্য দায়ী পদ্ধতি হল #interpret_function_call :

def interpret_function_call(fn_call)
  return if println(fn_call)
end

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

def println(fn_call)
  return false if fn_call.function_name_as_str != 'println'

  result = interpret_node(fn_call.args.first).to_s
  output << result
  puts result
  true
end

আমাদের AST::FunctionCall এর প্রথম এবং একমাত্র যুক্তি একটি AST::String , যা #interpret_string দ্বারা পরিচালিত হয় :

def interpret_string(string)
  string.value
end

#interpret_string-এ , আমাদের কাছে #interpret_number-এর ক্ষেত্রে ঠিক একই রকম আছে . একটি AST::String ইতিমধ্যেই একটি ব্যবহার করার জন্য প্রস্তুত রুবি স্ট্রিং মান রয়েছে, তাই আমাদের কেবল এটি পুনরুদ্ধার করতে হবে৷

এখন, #println-এ ফিরে যান :

def println(fn_call)
  return false if fn_call.function_name_as_str != 'println'

  result = interpret_node(fn_call.args.first).to_s
  output << result
  puts result
  true
end

result এ ফাংশন আর্গুমেন্ট (একটি রুবি স্ট্রিং এ রূপান্তরিত) সংরক্ষণ করার পরে , আমাদের আরও দুটি ধাপ সম্পূর্ণ করতে হবে। প্রথমে, আমরা কনসোলে যা প্রিন্ট করতে যাচ্ছি তা @output-এ সংরক্ষণ করি . যেমনটি আগে ব্যাখ্যা করা হয়েছে, এখানে ধারণাটি হ'ল কী মুদ্রিত হয়েছিল (এবং কী ক্রমে) সহজেই পরিদর্শন করতে সক্ষম হওয়া। ডিবাগিং বা ইন্টারপ্রেটার পরীক্ষা করার সময় এটি হাতে থাকা আমাদের জীবনকে সহজ করে তোলে। অবশেষে, কনসোলে কিছু মুদ্রণ বাস্তবায়ন করতে, আমরা রুবির puts ব্যবহার করি .

সম্পাদনা সংক্রান্ত বিষয়

এখন যেহেতু আমরা স্টফলের বেয়ার-বোন বাস্তবায়নের জন্য প্রয়োজনীয় সমস্ত কিছু অন্বেষণ করেছি, আসুন আমাদের দোভাষীকে কাজ করতে দেখতে একটি খুব মৌলিক এক্সিকিউটেবল তৈরি করি৷

#!/usr/bin/env ruby

require_relative '../lib/stoffle'

path = ARGV[0]
source = File.read(path)
lexer = Stoffle::Lexer.new(source)
parser = Stoffle::Parser.new(lexer.start_tokenization)
interpreter = Stoffle::Interpreter.new

interpreter.interpret(parser.parse)

exit(0)

টিপ: যেকোনো জায়গা থেকে Stoffle এর দোভাষী ব্যবহার করতে, আপনার PATH-এ এক্সিকিউটেবল যোগ করতে ভুলবেন না।

এটি অবশেষে আমাদের প্রোগ্রাম চালানোর সময়. যদি সবকিছু ঠিকঠাক কাজ করে, তাহলে আমাদের কনসোলে প্রিন্ট করা "সংখ্যা শূন্যের কম বা সমান" স্ট্রিং দেখতে হবে। আমরা যখন দোভাষী চালাই তখন ঠিক এটিই ঘটে:

রুবিতে একটি নতুন প্রোগ্রামিং ভাষা তৈরি করা:দোভাষী

টিপ: যদি আপনার ইন্টারপ্রেটার ইনস্টল করা থাকে, তাহলে num পরিবর্তন করার চেষ্টা করুন আমাদের নমুনা প্রোগ্রামে পরিবর্তনশীল যাতে এটি শূন্যের চেয়ে বড় একটি সংখ্যা রাখে। প্রত্যাশিত হিসাবে, এখন IF শাখাটি কার্যকর হবে, এবং স্ট্রিংটি "শূন্যের চেয়ে বড়" প্রিন্ট করা হবে৷

র্যাপিং আপ

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


  1. রুবিতে কার্যকরী প্রোগ্রামিং (সম্পূর্ণ নির্দেশিকা)

  2. রুবি মধ্যে স্ট্যাটিক বিশ্লেষণ

  3. রুবি নেটওয়ার্ক প্রোগ্রামিং

  4. প্রোগ্রামিং ভাষার প্রভাব গ্রাফটি কল্পনা করুন