BNF ও EBNF নোটেশন
এই পাঠে যা শিখবেন
- BNF প্রোডাকশন রুলের সিনট্যাক্স —
::=,|, টার্মিনাল বনাম নন-টার্মিনাল - একটি সম্পূর্ণ arithmetic expression গ্রামার — এই কোর্সের পার্সিং মডিউলে বারবার ফিরে আসবে
- EBNF-এর
[X]ও{X}শর্টহ্যান্ড, এবং কেন এগুলো শুধু নোটেশনাল সুবিধা - Python দিয়ে EBNF-এর
{X}কে সমতুল্য প্লেইন-BNF রুলে যান্ত্রিক রূপান্তর
১ · BNF (Backus-Naur Form) কী
BNFBackus-Naur Formকনটেক্সট-ফ্রি গ্রামারের প্রোডাকশন রুল লেখার স্ট্যান্ডার্ড, ক্লাসিক নোটেশন — <nonterminal> ::= expansion আকারে লেখা হয়।
হলো L11-এ পরিচিত হওয়া গ্রামারের রুলগুলো লেখার একটি স্ট্যান্ডার্ড নোটেশন। প্রতিটি BNF রুলের আকার —
<nonterminal> ::= expansion
যেখানে ::=-এর ডানদিকের expansion হলো টার্মিনাল (আক্ষরিক
সিম্বল, যেমন + বা if) ও/অথবা নন-টার্মিনাল-এর (অ্যাংগেল ব্র্যাকেটে
লেখা প্লেসহোল্ডার, যেমন <expr>, যা অন্য কোনো রুলে আরও বিস্তারিতভাবে সংজ্ঞায়িত থাকে) একটি
সিকোয়েন্স। যখন একই নন-টার্মিনালের একাধিক বৈধ এক্সপ্যানশন থাকে, সেগুলো | চিহ্ন দিয়ে আলাদা করে একই
রুলে লেখা হয় — প্রতিটি |-এর দুই পাশ একটি করে বিকল্প (alternative)।
২ · Worked example — একটি arithmetic expression গ্রামার
নিচের গ্রামারটি একটি ক্লাসিক, স্ট্যান্ডার্ড টেক্সটবুক উদাহরণ — সাধারণ গাণিতিক এক্সপ্রেশনের সিনট্যাক্স বর্ণনা করে। এই ঠিক গ্রামারটি নোট করে রাখুন — এটি এই কোর্সের M5 (পার্সিং) মডিউলের প্রায় প্রতিটি পাঠে বারবার পুনরায় ব্যবহৃত হবে।
$$\langle expr \rangle ::= \langle expr \rangle\ {+}\ \langle term \rangle \mid \langle expr \rangle\ {-}\ \langle term \rangle \mid \langle term \rangle$$ $$\langle term \rangle ::= \langle term \rangle\ {*}\ \langle factor \rangle \mid \langle term \rangle\ {/}\ \langle factor \rangle \mid \langle factor \rangle$$ $$\langle factor \rangle ::= ({\ }\langle expr \rangle{\ }) \mid \text{number}$$
<factor>-এর রুলে ( <expr> ) — অর্থাৎ একটি বন্ধনীযুক্ত
সাব-এক্সপ্রেশনের ভেতরে আবার সম্পূর্ণ <expr> ব্যবহার করা যায়। এই রিকার্সিভ সংজ্ঞাই একটি
গ্রামারকে যেকোনো গভীরতার নেস্টেড এক্সপ্রেশন (যেমন ((1+2)*3)) বর্ণনা করার ক্ষমতা দেয় —
একটি finite সংখ্যক রুল দিয়ে একটি infinite ভাষা বর্ণনা করার ঠিক সেই কৌশল যা L11-এ উল্লেখ করা হয়েছিল।
৩ · EBNF (Extended BNF) — নোটেশনাল সুবিধা, নতুন ক্ষমতা নয়
EBNFExtended BNFপ্লেইন BNF-এর উপর সুবিধাজনক শর্টহ্যান্ড যোগ করা একটি নোটেশন — কোনো নতুন এক্সপ্রেসিভ পাওয়ার যোগ করে না, শুধু পড়া/লেখা সহজ করে। সাধারণ কিছু প্যাটার্ন (ঐচ্ছিক অংশ, পুনরাবৃত্তি) প্লেইন BNF-এ বারবার একই রিকার্সিভ প্যাটার্ন লিখতে বাধ্য করে। EBNF তিনটি শর্টহ্যান্ড যোগ করে —
[X] — ঐচ্ছিকX শূন্যবার অথবা ঠিক একবার — যেমন
[ else-block ]।{X} — পুনরাবৃত্তিX শূন্যবার অথবা তার বেশিবার — যেমন
{ statement }।( )একাধিক সিম্বলকে একসাথে গ্রুপ করে
|, [ ] বা { }-এর সাথে ব্যবহার করা যায়।
গুরুত্বপূর্ণ কথা — EBNF কোনো নতুন এক্সপ্রেসিভ পাওয়ার যোগ করে না। EBNF-এ লেখা যেকোনো কিছু
যান্ত্রিকভাবে প্লেইন BNF-এ রূপান্তরযোগ্য। যেমন {X}-কে একটি রিকার্সিভ প্লেইন-BNF রুল দিয়ে ঠিক
সমতুল্যভাবে লেখা যায় —
list ::= { item } ⟺ list ::= item list | ε
(এখানে ε মানে খালি স্ট্রিং — "কিছুই না।") নিচের কোড সেলে এই যান্ত্রিক রূপান্তরটি আসলে বাস্তবায়ন
করে দেখানো হবে।
# EBNF-এর { item } (পুনরাবৃত্তি) কে সমতুল্য প্লেইন-BNF রুলে রূপান্তর
def ebnf_repetition_to_bnf(nonterminal, item_symbol):
"""
EBNF শর্টহ্যান্ড { item } -কে প্লেইন-BNF রুলে রূপান্তর করে।
রুল = (lhs, [alt1, alt2, ...]) -- প্রতিটি alt একটি সিম্বলের টাপল, () মানে ε
list ::= item list | ε
"""
return (nonterminal, [
(item_symbol, nonterminal), # বিকল্প ১: item তারপর বাকি list
() # বিকল্প ২: ε -- তালিকা এখানেই শেষ
])
def matches_ebnf_repetition(tokens, item_symbol):
# EBNF { item } -- সরাসরি: প্রতিটি টোকেন item-এর সাথে মেলে কিনা
return all(tok == item_symbol for tok in tokens)
def matches_bnf_rule(tokens, item_symbol):
# জেনারেট করা প্লেইন-BNF রুল (list ::= item list | ε) রিকার্সিভভাবে যাচাই
if not tokens:
return True # বিকল্প ২: ε প্রযোজ্য
if tokens[0] == item_symbol:
return matches_bnf_rule(tokens[1:], item_symbol) # বিকল্প ১
return False
rule = ebnf_repetition_to_bnf("list", "item")
lhs, alternatives = rule
print("জেনারেট করা প্লেইন-BNF রুল:")
for i, alt in enumerate(alternatives):
rhs = " ".join(alt) if alt else "ε"
print(f" {lhs} {'::=' if i == 0 else '| '} {rhs}")
test_cases = [[], ["item"], ["item", "item"], ["item", "item", "item"], ["item", "x"]]
print("\nEBNF { item } বনাম জেনারেট করা প্লেইন-BNF রুল -- তুলনা:")
for tc in test_cases:
ebnf_ok = matches_ebnf_repetition(tc, "item")
bnf_ok = matches_bnf_rule(tc, "item")
same = "একমত ✓" if ebnf_ok == bnf_ok else "MISMATCH ✗"
print(f" {str(tc):32s} EBNF={ebnf_ok!s:5s} BNF={bnf_ok!s:5s} {same}")
লক্ষ্য করুন matches_bnf_rule ফাংশনটি একটি রিকার্সিভ ফাংশন — ঠিক যেভাবে
Data Structures & Algorithms কোর্সে রিকার্সিভ অ্যালগরিদম শেখানো হয়েছে।
BNF রুলের রিকার্সিভ গঠন (একটি নন-টার্মিনাল নিজের সংজ্ঞায় নিজেকে উল্লেখ করা) এবং রিকার্সিভ প্রোগ্রামের গঠন
প্রায় হুবহু একই রকম — M5/L22-এ (রিকার্সিভ ডিসেন্ট পার্সিং) এই সংযোগটি আরও স্পষ্ট হবে।
BNF হলো গ্রামার লেখার ভিত্তি নোটেশন — ::= ও |। EBNF শুধু সুবিধাজনক শর্টহ্যান্ড
([X], {X}) যোগ করে, কোনো নতুন ক্ষমতা নয়। এই পাঠের arithmetic গ্রামার
(<expr>/<term>/<factor>) মনে রাখুন — এটি এই
কোর্সের বাকি অংশে বারবার ফিরে আসবে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
<factor> ::= ( <expr> ) | number রুলে <expr> আবার ব্যবহার করা হয়েছে কেন এটি সমস্যা নয় — এটি কি অসীম লুপ তৈরি করে না?
এটি রিকার্সিভ, কিন্তু অসীম লুপ নয় — কারণ প্রতিবার <factor>-এর এই বিকল্পটি প্রয়োগ করতে
হলে ইনপুটে অবশ্যই একটি literal ( টার্মিনাল থাকতে হয়। প্রতিটি রিকার্সিভ কল ইনপুট স্ট্রিং-এর
একটি অংশ "খরচ" করে (একটি বন্ধনী)। যেহেতু ইনপুট স্ট্রিং সবসময় ফাইনাইট, বন্ধনীও ফাইনাইটবার নেস্টেড হতে
পারে — তাই রিকার্শন অবশ্যম্ভাবীভাবে থামে। এটিই একটি গ্রামারে "উৎপাদনশীল রিকার্শন" (প্রতিটি রিকার্সিভ ধাপে
অন্তত একটি টার্মিনাল consume হয়) বনাম L22-এ আলোচিত সমস্যাযুক্ত "left recursion"-এর মূল পার্থক্য।
প্র ০২ যদি EBNF কোনো নতুন এক্সপ্রেসিভ পাওয়ার না-ই যোগ করে, তাহলে কেন প্র্যাকটিসে মানুষ প্লেইন BNF-এর বদলে EBNF ব্যবহার করে?
পাঠযোগ্যতা (readability) ও লেখার সুবিধার জন্য (L04-এর ভাষায়: writability)। { statement }
একবার পড়েই বোঝা যায় "শূন্য বা তার বেশি স্টেটমেন্ট" — অথচ সমতুল্য প্লেইন-BNF রুল
(stmt-list ::= statement stmt-list | ε) পড়তে একটু বেশি মানসিক প্রচেষ্টা লাগে, বিশেষ করে
বড় গ্রামারে যেখানে এরকম বহু জায়গায় পুনরাবৃত্তি/ঐচ্ছিকতা আছে। যেহেতু দুটোই সমতুল্য, ভাষা-নির্দিষ্টকরণের
(specification) সময় মানুষ যেটা পড়তে সহজ সেটাই বেছে নেয় — এক্সপ্রেসিভ পাওয়ার একই থাকে বলে এই বেছে নেওয়া
কোনো "সীমাবদ্ধতা" তৈরি করে না।
প্র ০৩
উপরের কোড সেলে ["item", "x"] টেস্ট কেসে EBNF ও BNF উভয়েই False ফেরত দেয় কেন — "item" তো মিলেছে?
{ item } মানে "শূন্য বা তার বেশি item, এবং শুধুই item" — তালিকার প্রতিটি টোকেন
অবশ্যই item হতে হবে, শুধু প্রথমটি মিললেই যথেষ্ট নয়। matches_ebnf_repetition-এ
all(...) ব্যবহার করা হয়েছে বলে একটি অমিল থাকলেই পুরো ফলাফল False হয়ে যায়।
matches_bnf_rule-এও একই যুক্তি — "item" মিলে গেলে বাকি অংশে (["x"]) রিকার্সিভ
কল হয়, এবং সেখানে "x" != "item" হওয়ায় সরাসরি False ফেরত আসে। দুটো ফাংশনই
স্বাধীনভাবে লেখা হলেও একই সিদ্ধান্তে পৌঁছায় — কারণ তারা একই ভাষা বর্ণনা করছে।
অনুশীলন
-
হাতে-কলমে লিখুন: L12-এর arithmetic গ্রামার ব্যবহার করে
<factor>-এর জন্য একটি নতুন নন-টার্মিনাল<factor> ::= - <factor> | ( <expr> ) | numberযোগ করে ইউনারি মাইনাস (যেমন-5) সাপোর্ট করার BNF রুল লিখুন।রুলটি ঠিক এভাবেই লেখা যায়:
<factor> ::= - <factor> | ( <expr> ) | number। লক্ষ্য করুন এটিও রিকার্সিভ (<factor>নিজের সংজ্ঞায় নিজেকে উল্লেখ করছে) কিন্তু উৎপাদনশীল — প্রতিটি রিকার্শন একটি-টার্মিনাল consume করে, তাই---5-এর মতো ইনপুটও (তিনবার ইউনারি মাইনাস) সসীম ধাপে টার্মিনেট হয়। -
পরীক্ষা করুন: উপরের কোড সেলে
test_cases-এ["item", "item", "x", "item"]যোগ করে Run চেপে দেখুন ফলাফল কী আসে এবং কেন।দুটো ফাংশনই
Falseফেরত দেবে। কারণ তৃতীয় টোকেন"x"item নয় — যদিও এর পরে আবার একটি বৈধ"item"আছে, একটিমাত্র অ-item টোকেনও পুরো স্ট্রিংকে{ item }-এর ভাষার বাইরে ফেলে দেয়, কারণ এই ভাষায় item ছাড়া অন্য কিছু থাকার অনুমতি নেই।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ পরবর্তী পাঠ — কনটেক্সট-ফ্রি গ্রামার ও ডেরিভেশন — এই একই arithmetic গ্রামার ব্যবহার করে দেখাবে কীভাবে একটি স্ট্রিং ধাপে ধাপে "উৎপন্ন" করা হয়।
- পাঠ ১১ · ফরমাল ল্যাঙ্গুয়েজ ও গ্রামার পরিচিতি পূর্ববর্তী পাঠ অ্যালফাবেট, স্ট্রিং ও ল্যাঙ্গুয়েজের মৌলিক সংজ্ঞা — BNF নোটেশন বোঝার আগে এই ভিত্তিটুকু জরুরি।
- Discrete Mathematics কোর্স তাত্ত্বিক পূর্বসূরি ফরমাল গ্রামার ও রেগুলার ল্যাঙ্গুয়েজের গভীর গাণিতিক ভিত্তি এই কোর্সে কভার করা হয়েছে।