অ্যাবস্ট্রাক্ট সিনট্যাক্স ট্রি (AST), যাকে কোটেড এক্সপ্রেশন-ও বলা হয়, হলো কোডকে ডেটা হিসেবে প্রকাশ করার একটি উপায়।
AST-এর প্রতিটি নোড তিনটি এলিমেন্টের একটি টাপল।
# AST representation of:
# 2 + 3
{:+, [], [2, 3]}
প্রথম এলিমেন্টটি, একটি অ্যাটম, হলো অপারেশন। দ্বিতীয় এলিমেন্টটি, একটি কিওয়ার্ড লিস্ট, হলো মেটাডেটা। তৃতীয় এলিমেন্টটি আর্গুমেন্টের একটি অ্যারে, যেটিতে অন্য নোড থাকে। ইন্টিজার, অ্যাটম ও স্ট্রিংয়ের মতো লিটারাল মান AST-এ তিনটি এলিমেন্টের টাপল হিসেবে নয়, বরং নিজেদের রূপেই উপস্থাপিত হয়।
Elixir কোডকে AST-এ এবং AST-কে আবার কোডে বদলানো স্ট্যান্ডার্ড লাইব্রেরির অংশ। AST নিয়ে কাজ করার ফাংশন আপনি Code (যেমন কোডসহ একটি স্ট্রিংকে AST-এ বদলানো) এবং Macro (যেমন AST ট্রাভার্স করা বা এটিকে স্ট্রিংয়ে বদলানো) মডিউলে পাবেন।
মনে রাখবেন, স্ট্যান্ডার্ড লাইব্রেরির সব ফাংশনই AST বোঝাতে "quoted" নামটি ব্যবহার করে (quoted expression-এর সংক্ষিপ্ত রূপ)।
কোডকে AST-এ রূপান্তর করার স্পেশাল ফর্মটির নাম quote। এটি একটি কোড ব্লক নেয় এবং তার AST রিটার্ন করে।
quote do
2 + 3 - 1
end
# => {:-, [], [
# {:+, [], [2, 3]},
# 1
# ]}
কোডকে AST হিসেবে উপস্থাপন করার ক্ষমতাই Elixir-এ মেটাপ্রোগ্রামিংয়ের মূল ভিত্তি। ম্যাক্রো হলো Elixir কোড লেখার এমন এক উপায়, যা Elixir কোড তৈরি করে; এগুলো আউটপুট হিসেবে AST রিটার্ন করে কাজ করে।
AST-এর আরেকটি ব্যবহারের ক্ষেত্র হলো স্ট্যাটিক কোড অ্যানালাইসিস, যেমন Exercism-এর নিজস্ব টুল Analyzer, যেটিকে আপনি হয়তো ইতিমধ্যেই চেনেন ছোট্ট সেই বট হিসেবে, যা আপনার সলিউশনের ওপর কমেন্ট করে।
আপনি কর্পোরেট গুপ্তচরবৃত্তির বিরুদ্ধে লড়াই করা একটি টাস্ক ফোর্সের অংশ। Shady Company X-এ আপনার একজন গোপন তথ্যদাতা আছেন, যাকে আপনি সন্দেহ করেন তার প্রতিযোগীদের কাছ থেকে গোপন তথ্য চুরি করছে।
আপনার তথ্যদাতা, এজেন্ট এক্স, একজন Elixir ডেভেলপার। তিনি তাঁর কোডে গোপন বার্তা এনকোড করছেন।
তাঁর গোপন বার্তাগুলো ডিকোড করতে হলে:
n ক্যারেক্টার নিন, যেখানে n হলো ফাংশনটির অ্যারিটি।TopSecret.to_ast/1 ফাংশনটি ইমপ্লিমেন্ট করুন। এটি Elixir কোডসহ একটি স্ট্রিং নেবে এবং তার AST রিটার্ন করবে।
TopSecret.to_ast("div(4, 3)")
# => {:div, [line: 1], [4, 3]}
TopSecret.decode_secret_message_part/2 ফাংশনটি ইমপ্লিমেন্ট করুন। এটি একটি AST নোড এবং গোপন বার্তার জন্য একটি অ্যাকুমুলেটর (একটি অ্যারে) নেবে। এটি একটি টাপল রিটার্ন করবে, যার প্রথম এলিমেন্ট হবে অপরিবর্তিত AST নোডটি এবং দ্বিতীয় এলিমেন্ট হবে অ্যাকুমুলেটরটি।
যদি AST নোডের অপারেশন ফাংশন ডিফাইন করা বোঝায় (def বা defp), তাহলে ফাংশনের নামটি (স্ট্রিংয়ে পরিবর্তিত) অ্যাকুমুলেটরের সামনে যোগ করুন। যদি অপারেশনটি অন্য কিছু হয়, তাহলে অ্যাকুমুলেটরটি অপরিবর্তিত রিটার্ন করুন।
ast_node = TopSecret.to_ast("defp cat(a, b, c), do: nil")
TopSecret.decode_secret_message_part(ast_node, ["day"])
# => {ast_node, ["cat", "day"]}
ast_node = TopSecret.to_ast("10 + 3")
TopSecret.decode_secret_message_part(ast_node, ["day"])
# => {ast_node, ["day"]}
এই ফাংশনটিকে পুরো AST যাচাই করার জন্য কোনো রিকার্সিভ কল করতে হবে না, শুধু দেওয়া নোডটিই যথেষ্ট। শেষ ধাপে আমরা বিল্ট-ইন টুল দিয়ে পুরো AST ট্রাভার্স করব।
TopSecret.decode_secret_message_part/2 ফাংশনটি সম্প্রসারিত করুন। যদি AST নোডের অপারেশন ফাংশন ডিফাইন করা বোঝায়, তাহলে পুরো ফাংশনের নাম রিটার্ন করবেন না। বরং ফাংশনটির অ্যারিটি পরীক্ষা করুন। তারপর নাম থেকে শুধু প্রথম n ক্যারেক্টার রিটার্ন করুন, যেখানে n হলো অ্যারিটি।
ast_node = TopSecret.to_ast("defp cat(a, b), do: nil")
TopSecret.decode_secret_message_part(ast_node, ["day"])
# => {ast_node, ["ca", "day"]}
ast_node = TopSecret.to_ast("defp cat(), do: nil")
TopSecret.decode_secret_message_part(ast_node, ["day"])
# => {ast_node, ["", "day"]}
TopSecret.decode_secret_message_part/2 ফাংশনটি সম্প্রসারিত করুন। গার্ড ব্যবহার করা ফাংশন ডেফিনিশনের জন্য ফাংশনের নাম ও অ্যারিটি যেন সঠিকভাবে শনাক্ত হয়, তা নিশ্চিত করুন।
ast_node = TopSecret.to_ast("defp cat(a, b) when is_nil(a), do: nil")
TopSecret.decode_secret_message_part(ast_node, ["day"])
# => {ast_node, ["ca", "day"]}
TopSecret.decode_secret_message/1 ফাংশনটি ইমপ্লিমেন্ট করুন। এটি Elixir কোডসহ একটি স্ট্রিং নেবে এবং কোডে পাওয়া সব ফাংশন ডেফিনিশন থেকে ডিকোড করা গোপন বার্তাটি একটি স্ট্রিং হিসেবে রিটার্ন করবে। আগের ধাপগুলোতে ডিফাইন করা ফাংশনগুলো যেন পুনরায় ব্যবহার করেন, তা নিশ্চিত করুন।
code = """
defmodule MyCalendar do
def busy?(date, time) do
Date.day_of_week(date) != 7 and
time.hour in 10..16
end
def yesterday?(date) do
Date.diff(Date.utc_today, date)
end
end
"""
TopSecret.decode_secret_message(code)
# => "buy"
Exercism-এ সাইন আপ করুন, Elixir ট্র্যাকের 58টি কনসেপ্ট168টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।