ব্যাকরণের উপর ভিত্তি করে কোনও লেক্সার লেখার সময় কী পদ্ধতি অনুসরণ করা হয়?


13

ব্যাকরণ, লেক্সারস এবং পার্সার্স সম্পর্কে প্রশ্নের জবাবে পাঠ করার সময় , উত্তরে বলা হয়েছে:

[...] একটি বিএনএফ ব্যাকরণে আপনার কাছে লেক্সিকাল বিশ্লেষণ এবং বিশ্লেষণের জন্য প্রয়োজনীয় সমস্ত বিধি রয়েছে।

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

যদি লেক্সারগুলি পাশাপাশি পার্সারগুলি কোনও ইবিএনএফ / বিএনএফ ব্যাকরণের উপর ভিত্তি করে থাকে, তবে কীভাবে কেউ এই পদ্ধতিটি ব্যবহার করে কোনও লেক্সার তৈরি করতে চলেছেন? অর্থাত, প্রদত্ত ইবিএনএফ / বিএনএফ ব্যাকরণ ব্যবহার করে আমি কীভাবে একটি লেসিকার তৈরি করব?

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

উদাহরণস্বরূপ, নিম্নলিখিত ব্যাকরণ গ্রহণ করুন:

input = digit| string ;
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
string = '"', { all characters - '"' }, '"' ;
all characters = ? all visible characters ? ;

ব্যাকরণের উপর ভিত্তি করে কেউ কীভাবে একটি লেক্সার তৈরি করতে পারে? আমি ভাবতে পারি যে এই জাতীয় ব্যাকরণ থেকে কীভাবে পার্সার লেখা যেতে পারে, তবে আমি কোনও লেসারের সাহায্যে এটি করার ধারণাটি বুঝতে ব্যর্থ হয়েছি।

পার্সার লেখার মতো কোনও কাজ সম্পাদন করার জন্য কি কিছু বিধি বা যুক্তি ব্যবহৃত হয়? সত্যি বলতে গেলে, আমি ভাবতে শুরু করি যে লেক্সার ডিজাইনগুলি কোনও ইবিএনএফ / বিএনএফ ব্যাকরণ আদৌ ব্যবহার করে কিনা, একাকী একটিকে ভিত্তি করে দেওয়া হোক।


1 বর্ধিত ব্যাকাস – নওর ফর্ম এবং ব্যাকাস – নওর ফর্ম

উত্তর:


18

লেক্সারগুলি কেবলমাত্র সাধারণ পার্সার যা মূল পার্সারের পারফরম্যান্স অপটিমাইজেশন হিসাবে ব্যবহৃত হয়। আমাদের যদি কোনও লেক্সার থাকে তবে লেক্সার এবং পার্সার একসাথে সম্পূর্ণ ভাষাটি বর্ণনা করার জন্য কাজ করে। পার্সারদের, যাদের আলাদা লেক্সিং স্টেজ থাকে না, তাদের মাঝে মাঝে "স্ক্যানারলেস" বলা হয়।

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

যেহেতু একটি চরিত্রের বাইরের চরিত্রের ভিত্তিতে পাঠ্য মোটামুটি অস্পষ্ট, তাই এটি হ'ল বিরক্তিকর এমন আরও অনেক অস্পষ্টতারও ফলস্বরূপ। একটি নিয়ম কল্পনা করুন 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 টোকেন এবং ব্লকটি শেষ হয়ে গেলে ডিডেন্ট টোকেনগুলি বের করে। এটি মূল ব্যাকরণকে সহজতর করে কারণ এটি এখন সেই টোকেনগুলি কোঁকড়া ধনুর্বন্ধনী যেমন। লেক্সারের অবশ্য এখন অবস্থা বজায় রাখা দরকার: বর্তমান ইনডেন্টেশন। এর অর্থ এই ল্যাক্সার প্রযুক্তিগতভাবে আর কোনও নিয়মিত ভাষার বর্ণনা দেয় না, তবে প্রকৃতপক্ষে একটি প্রসঙ্গ-সংবেদনশীল ভাষা। ভাগ্যক্রমে এই পার্থক্যটি অনুশীলনে প্রাসঙ্গিক নয় এবং পাইথনের লিক্সার এখনও ও (এন) সময়ে কাজ করতে পারে।


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

@ ইঞ্জিনিয়ার আমি কয়েকটি সম্পাদনা করেছি। আপনার ইবিএনএফ সরাসরি কোনও পার্সার ব্যবহার করতে পারেন। যাইহোক, আমার উদাহরণটি দেখায় যে ব্যাকরণের কোন অংশগুলি পৃথক লেক্সার দ্বারা পরিচালিত হতে পারে। অন্য কোনও বিধিগুলি এখনও প্রধান পার্সার দ্বারা পরিচালিত হবে, তবে আপনার উদাহরণে এটি ঠিক input = digit | string
আমন

4
স্ক্যানারহীন পার্সারগুলির বড় সুবিধা হ'ল তারা রচনা করা আরও সহজ; যে চরম উদাহরণ পার্সার combinator লাইব্রেরি, যেখানে আপনি কিছুই করতে হয় কিন্তু রচনা পারজার। সংশ্লেষ পার্সার করা যেমন ECMAScript- এমবেডড-ইন-এইচটিএমএল-এমবেডড-ইন-পিএইচপি-এর সাথে এসকিউএল-এর সাথে-এ-টেমপ্লেট-ল্যাঙ্গুয়েজে-ভাষা-তে-শীর্ষে বা রুবি-উদাহরণ-এম্বেড-ইন-মার্কডাউন- এম্বেড-ইন-রুবি-ডকুমেন্টেশন-মন্তব্য বা এর মতো কিছু।
Jörg ডব্লু মিট্টাগ

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

@ মেহরদাদ পাইথন-স্টাইলের লেসার-চালিত ইনডেন্ট / ডেডিয়েট টোকেন কেবল খুব সাধারণ ইনডেন্টেশন-সংবেদনশীল ভাষার জন্য সম্ভব এবং সাধারণত প্রযোজ্য নয়। আরও সাধারণ বিকল্প হ'ল বৈশিষ্ট্য ব্যাকরণ, তবে তাদের সমর্থনটিতে স্ট্যান্ডার্ড সরঞ্জামগুলির অভাব রয়েছে। ধারণাটি হ'ল আমরা প্রতিটি এএসটি খণ্ডকে এর ইনডেন্টেশন দিয়ে টীকায়িত করি এবং সমস্ত নিয়মে সীমাবদ্ধতা যুক্ত করি। সমন্বয়কারী পার্সিংয়ের সাথে যুক্ত করা সহজ বৈশিষ্ট্য, যা স্ক্যানারহীন পার্সিংও সহজ করে তোলে it
amon
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.