লেক্সারগুলি কেবলমাত্র সাধারণ পার্সার যা মূল পার্সারের পারফরম্যান্স অপটিমাইজেশন হিসাবে ব্যবহৃত হয়। আমাদের যদি কোনও লেক্সার থাকে তবে লেক্সার এবং পার্সার একসাথে সম্পূর্ণ ভাষাটি বর্ণনা করার জন্য কাজ করে। পার্সারদের, যাদের আলাদা লেক্সিং স্টেজ থাকে না, তাদের মাঝে মাঝে "স্ক্যানারলেস" বলা হয়।
লেক্সারগুলি ছাড়াই পার্সারকে একটি চরিত্র অনুসারে চরিত্রের ভিত্তিতে পরিচালনা করতে হবে। যেহেতু পার্সারকে প্রতিটি ইনপুট আইটেম সম্পর্কে মেটাডেটা সংরক্ষণ করতে হয় এবং প্রতিটি ইনপুট আইটেমের স্থিতির জন্য টেবিলগুলি প্রাক-গণনা করতে হতে পারে, এর ফলে বড় ইনপুট আকারের জন্য অগ্রহণযোগ্য মেমরির খরচ হবে। বিশেষত, বিমূর্ত সিনট্যাক্স ট্রিতে আমাদের প্রতিটি চরিত্রের জন্য পৃথক নোডের দরকার নেই।
যেহেতু একটি চরিত্রের বাইরের চরিত্রের ভিত্তিতে পাঠ্য মোটামুটি অস্পষ্ট, তাই এটি হ'ল বিরক্তিকর এমন আরও অনেক অস্পষ্টতারও ফলস্বরূপ। একটি নিয়ম কল্পনা করুন R → identifier | "for " identifier
। যেখানে শনাক্তকারীকে ASCII অক্ষর থেকে তৈরি করা হয়। আমি যদি অস্পষ্টতা এড়াতে চাই তবে আমার বিকল্পটি বেছে নেওয়া উচিত তা নির্ধারণ করার জন্য এখন আমার একটি 4-চরিত্রের লুকআপ দরকার। কোনও লেক্সারের সাথে, পার্সারকে কেবল এটির একটি আইডেন্টিফায়ার আছে বা ফর টোকেন আছে কিনা তা যাচাই করতে হবে - 1-টোকেন লুক্কায়িত।
দ্বি-স্তরের ব্যাকরণ।
লেক্সাররা ইনপুট বর্ণমালা আরও সুবিধাজনক বর্ণমালায় অনুবাদ করে কাজ করে।
একটি স্ক্যানারবিহীন পার্সার একটি ব্যাকরণকে বর্ণনা করে (এন, Σ, পি, এস) যেখানে নন-টার্মিনালগুলি এন ব্যাকরণের নিয়মের বাম দিক, বর্ণমালা Σ যেমন ASCII অক্ষর, প্রযোজনাগুলি ব্যাকরণের নিয়ম , এবং সূচনা প্রতীক এস পার্সার শীর্ষ স্তরের নিয়ম।
লেক্সার এখন টোকেনের একটি, বি, সি,… এর বর্ণমালা সংজ্ঞায়িত করে। এটি মূল পার্সারকে এই টোকেনগুলিকে বর্ণমালা হিসাবে ব্যবহার করতে দেয়: Σ = {a, b, c,…}। লেক্সারের জন্য, এই টোকেনগুলি নন-টার্মিনালগুলি এবং শুরুর নিয়ম এস এল হ'ল এস এল → ε | a এস | খ এস | সি এস | …, এটি হ'ল: টোকেনগুলির যে কোনও ক্রম। লেকসার ব্যাকরণের নিয়মগুলি এই টোকেনগুলি উত্পাদন করার জন্য সমস্ত নিয়ম।
নিয়মিত ভাষা হিসাবে লেকসারের বিধিগুলি প্রকাশ করে পারফরম্যান্স সুবিধাটি আসে । এগুলি প্রসঙ্গ-মুক্ত ভাষার চেয়ে অনেক বেশি দক্ষতার সাথে পার্স করা যায়। বিশেষত, নিয়মিত ভাষাগুলি ও (এন) স্পেস এবং ও (এন) সময়ে স্বীকৃত হতে পারে। অনুশীলনে, একটি কোড জেনারেটর এই জাতীয় লেসারেরকে অত্যন্ত দক্ষ জাম্প টেবিলগুলিতে পরিণত করতে পারে।
আপনার ব্যাকরণ থেকে টোকেন নিষ্কাশন করা।
আপনার উদাহরণটি স্পর্শ করার জন্য: digit
এবং string
নিয়মগুলি একটি চরিত্র দ্বারা অক্ষর স্তরে প্রকাশ করা হয়। আমরা টোকেন হিসাবে ব্যবহার করতে পারে। ব্যাকরণের বাকি অংশ অক্ষত থাকে। এটি নিয়মিত যে এটি নিয়মিত করার জন্য ডান-লিনিয়ার ব্যাকরণ হিসাবে রচিত লেক্সার ব্যাকরণ এখানে রয়েছে:
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
string = '"' , string-rest ;
string-rest = '"' | STRING-CHAR, string-rest ;
STRING-CHAR = ? all visible characters ? - '"' ;
তবে এটি নিয়মিত হওয়ায় আমরা সাধারণত টোকেন সিনট্যাক্সটি প্রকাশ করার জন্য নিয়মিত এক্সপ্রেশন ব্যবহার করি। রেটেক্সেস হিসাবে উপরের টোকেন সংজ্ঞাগুলি এখানে রয়েছে। নেট অক্ষর শ্রেণীর বর্জন সিনট্যাক্স এবং পসিক্স চারক্লাস ব্যবহার করে লিখিত:
digit ~ [0-9]
string ~ "[[:print:]-["]]*"
মূল পার্সারের ব্যাকরণে লেসারের দ্বারা পরিচালিত নয় এমন অন্যান্য নিয়ম রয়েছে। আপনার ক্ষেত্রে, এটি ঠিক:
input = digit | string ;
যখন লেক্সারগুলি সহজে ব্যবহার করা যায় না।
কোনও ভাষা ডিজাইন করার সময়, আমরা সাধারণত যত্ন নিই যে ব্যাকরণটি পরিষ্কারভাবে একটি লেক্সার স্তর এবং পার্সার স্তরকে আলাদা করা যায় এবং লেক্সার স্তরটি একটি নিয়মিত ভাষার বর্ণনা দেয়। এই সবসময় সম্ভব হয় না।
ভাষা এম্বেড যখন। কিছু কিছু ভাষায় আপনি স্ট্রিং মধ্যে কোড ঢুকান করার মঞ্জুরি দিন: "name={expression}"
। এক্সপ্রেশন সিনট্যাক্স প্রসঙ্গমুক্ত ব্যাকরণের অংশ এবং তাই নিয়মিত প্রকাশের মাধ্যমে টোকনাইজ করা যায় না। এটি সমাধানের জন্য, আমরা হয় লেক্সারের সাথে পার্সারটিকে পুনরায় সংযুক্ত করি, অথবা আমরা অতিরিক্ত টোকেনগুলি প্রবর্তন করি STRING-CONTENT, INTERPOLATE-START, INTERPOLATE-END
। একটি স্ট্রিং জন্য ব্যাকরণ নিয়ম তারপর অনুযায়ী প্রদর্শিত হবে: String → STRING-START STRING-CONTENTS { INTERPOLATE-START Expression INTERPOLATE-END STRING-CONTENTS } STRING-END
। অবশ্যই এক্সপ্রেশনটিতে অন্যান্য স্ট্রিং থাকতে পারে যা আমাদের পরবর্তী সমস্যার দিকে নিয়ে যায়।
যখন টোকেনগুলি একে অপরকে ধারণ করতে পারে। সি-জাতীয় ভাষায়, কীওয়ার্ড শনাক্তকারীদের থেকে পৃথক করা যায়। শনাক্তকারীদের উপর কীওয়ার্ডকে অগ্রাধিকার দিয়ে লেক্সারে এটি সমাধান করা হয়। এ জাতীয় কৌশল সর্বদা সম্ভব হয় না। এমন একটি কনফিগার ফাইলের কল্পনা করুন যেখানে Line → IDENTIFIER " = " REST
রেখার শেষ অবধি বাকিগুলি কোনও অক্ষর, এমনকি বাকীটি সনাক্তকারী হিসাবে দেখায়। একটি উদাহরণ লাইন হবে a = b c
। লেক্সারটি সত্যিই বোবা এবং টোকেনগুলি কোন ক্রমে সংঘটিত হতে পারে তা জানে না। সুতরাং আমরা যদি বিশ্রামের চেয়ে আইডেন্টিফায়ারটিকে অগ্রাধিকার দিই তবে লেক্সার আমাদের দেবে IDENT(a), " = ", IDENT(b), REST( c)
। আমরা যদি আইডিএনটিফায়ারের তুলনায় আরআরএসটিকে অগ্রাধিকার দিই তবে লেক্সার কেবল আমাদের দেবে REST(a = b c)
।
এটি সমাধানের জন্য, আমাদেরকে পার্সারের সাথে লেক্সারটি পুনরায় সংযুক্ত করতে হবে। লেক্সারকে অলস করে কিছুটা পৃথকীকরণ বজায় রাখা যায়: প্রতিবার যখন পার্সার পরবর্তী টোকেনের প্রয়োজন হয় তখন এটি লেক্সারের কাছ থেকে এটি অনুরোধ করে এবং লেক্সারের কাছে গ্রহণযোগ্য টোকেনের সেটকে বলে। কার্যকরভাবে, আমরা প্রতিটি পদের জন্য লেক্সার ব্যাকরণের জন্য একটি নতুন শীর্ষ-স্তরের নিয়ম তৈরি করছি। এখানে, এর ফলে কল আসবে nextToken(IDENT), nextToken(" = "), nextToken(REST)
এবং সবকিছু ঠিকঠাক কাজ করবে। এর জন্য এমন একটি পার্সার প্রয়োজন যা প্রতিটি স্থানে গ্রহণযোগ্য টোকেনগুলির সম্পূর্ণ সেটটি জানে, যা এলআর এর মতো একটি ডাউন পার্সার বোঝায়।
যখন লেক্সারকে রাষ্ট্র বজায় রাখতে হয়। উদাহরণস্বরূপ পাইথন ল্যাঙ্গুয়েজ কোঁকড়া ধনুর্বন্ধনী দ্বারা নয় কোড অবলম্বন করে ind ব্যাকরণের মধ্যে বিন্যাস-সংবেদনশীল সিনট্যাক্স পরিচালনা করার উপায় রয়েছে তবে সেই কৌশলগুলি পাইথনের জন্য ওভারকিল। পরিবর্তে, লেক্সার প্রতিটি লাইনের ইনডেন্টেশন পরীক্ষা করে এবং নতুন ইনডেন্টড ব্লক পাওয়া গেলে INDENT টোকেন এবং ব্লকটি শেষ হয়ে গেলে ডিডেন্ট টোকেনগুলি বের করে। এটি মূল ব্যাকরণকে সহজতর করে কারণ এটি এখন সেই টোকেনগুলি কোঁকড়া ধনুর্বন্ধনী যেমন। লেক্সারের অবশ্য এখন অবস্থা বজায় রাখা দরকার: বর্তমান ইনডেন্টেশন। এর অর্থ এই ল্যাক্সার প্রযুক্তিগতভাবে আর কোনও নিয়মিত ভাষার বর্ণনা দেয় না, তবে প্রকৃতপক্ষে একটি প্রসঙ্গ-সংবেদনশীল ভাষা। ভাগ্যক্রমে এই পার্থক্যটি অনুশীলনে প্রাসঙ্গিক নয় এবং পাইথনের লিক্সার এখনও ও (এন) সময়ে কাজ করতে পারে।