সি ++ তে একটি লেক্সার লেখা


18

সি ++ (বই, টিউটোরিয়াল, ডকুমেন্টস) এ কীভাবে লেক্সার লিখতে হয় তার ভাল সংস্থানগুলি কী কী কিছু ভাল কৌশল এবং অনুশীলনগুলি কী?

আমি ইন্টারনেটে দেখেছি এবং প্রত্যেকে লেক্সের মতো একটি লেক্সার জেনারেটর ব্যবহার করতে বলেছে। আমি এটি করতে চাই না, আমি হাতে একটি লেক্সার লিখতে চাই।


ঠিক আছে, কেন আপনার উদ্দেশ্যে লেক্স ভাল নয়?
কারনেকোড

13
আমি কীভাবে লেক্সাররা কাজ করে তা শিখতে চাই। আমি কোনও লেক্সার জেনারেটর দিয়ে এটি করতে পারি না।
রাইটফোল্ড

11
লেক্স জঘন্য সি কোড উত্পন্ন করে। যে কোনও শালীন লেক্সার চায় সে লেক্স ব্যবহার করে না।
ডেডএমজি

5
@ জর্জিও: উত্সাহিত কোডটি হ'ল ঘৃণ্য নন-থ্রেড-নিরাপদ গ্লোবাল ভেরিয়েবলগুলির সাথে আপনাকে ইন্টারফেস করতে হবে এমন কোড, উদাহরণস্বরূপ, এবং এটি কোড যার নাল-টার্মিনেশন বাগগুলি আপনি আপনার অ্যাপ্লিকেশনটিতে প্রবর্তন করছেন।
ডেডএমজি

1
@ জর্জিও: আপনি কি কখনও লেক্সের দ্বারা কোড আউটপুট ডিবাগ করতে পারেন?
mattnz

উত্তর:


7

মনে রাখবেন যে প্রতিটি সীমাবদ্ধ রাষ্ট্রীয় মেশিন একটি নিয়মিত অভিব্যক্তির সাথে মিলে যায়, যা ব্যবহার করে কাঠামোগত প্রোগ্রামের সাথে মিলে যায় ifwhile বিবৃতি এবং বিবৃতি ।

সুতরাং, উদাহরণস্বরূপ, পূর্ণসংখ্যাগুলি সনাক্ত করতে আপনার কাছে রাষ্ট্রের মেশিন থাকতে পারে:

0: digit -> 1
1: digit -> 1

বা নিয়মিত প্রকাশ:

digit digit*

বা কাঠামোগত কোড:

if (isdigit(*pc)){
  while(isdigit(*pc)){
    pc++;
  }
}

ব্যক্তিগতভাবে, আমি সর্বদা উত্তরোত্তর ব্যবহার করে লেক্সার লিখি, কারণ আইএমএইচও এটি কোনও পরিষ্কার নয়, এবং এর চেয়ে দ্রুত আর কিছুই নেই।


আমি মনে করি যে নিয়মিত প্রকাশটি খুব জটিল হয়ে উঠলে সংশ্লিষ্ট কোডটিও হয়। এই কারণেই লেক্সার জেনারেটরটি ভাল: ভাষাটি খুব সহজ হলে আমি সাধারণত একটি লেক্সারকেই কোড করব।
জর্জিও

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

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

1
আপনি যে স্ট্রিং যুক্ত *pcকরবেন তাই না? ভালো লেগেছে while(isdigit(*pc)) { value += pc; pc++; }। তারপরে }আপনি মানটিকে একটি সংখ্যায় রূপান্তর করুন এবং একটি টোকনে সেট করুন।
ডানফোল্ড

@ ডব্লিউটিপি: সংখ্যার জন্য, আমি কেবল তাদের মতোই ফ্লাইতে গণনা করি n = n * 10 + (*pc++ - '0');। এটি ভাসমান পয়েন্ট এবং 'ই' স্বীকৃতির জন্য আরও কিছুটা জটিল হয়ে ওঠে তবে খারাপ নয়। আমি নিশ্চিত যে অক্ষরগুলিকে বাফারে এবং কল করে atofবা যাই হোক না কেন কিছু প্যাক করে আমি একটি ছোট কোড সংরক্ষণ করতে পারি । এটি কোনও দ্রুত চালিত হবে না।
মাইক ডুনলাভে

9

লেক্সারস সীমাবদ্ধ রাষ্ট্র মেশিন state সুতরাং এগুলি যে কোনও সাধারণ-উদ্দেশ্যমূলক এফএসএম গ্রন্থাগার দ্বারা নির্মিত যেতে পারে। আমার নিজের শিক্ষার উদ্দেশ্যে, তবে আমি নিজের লেখাটি প্রকাশ করেছি, এক্সপ্রেশন টেম্পলেট ব্যবহার করে। এখানে আমার লেক্সার:

static const std::unordered_map<Unicode::String, Wide::Lexer::TokenType> reserved_words(
    []() -> std::unordered_map<Unicode::String, Wide::Lexer::TokenType>
    {
        // Maps reserved words to TokenType enumerated values
        std::unordered_map<Unicode::String, Wide::Lexer::TokenType> result;

        // RESERVED WORD
        result[L"dynamic_cast"] = Wide::Lexer::TokenType::DynamicCast;
        result[L"for"] = Wide::Lexer::TokenType::For;
        result[L"while"] = Wide::Lexer::TokenType::While;
        result[L"do"] = Wide::Lexer::TokenType::Do;
        result[L"continue"] = Wide::Lexer::TokenType::Continue;
        result[L"auto"] = Wide::Lexer::TokenType::Auto;
        result[L"break"] = Wide::Lexer::TokenType::Break;
        result[L"type"] = Wide::Lexer::TokenType::Type;
        result[L"switch"] = Wide::Lexer::TokenType::Switch;
        result[L"case"] = Wide::Lexer::TokenType::Case;
        result[L"default"] = Wide::Lexer::TokenType::Default;
        result[L"try"] = Wide::Lexer::TokenType::Try;
        result[L"catch"] = Wide::Lexer::TokenType::Catch;
        result[L"return"] = Wide::Lexer::TokenType::Return;
        result[L"static"] = Wide::Lexer::TokenType::Static;
        result[L"if"] = Wide::Lexer::TokenType::If;
        result[L"else"] = Wide::Lexer::TokenType::Else;
        result[L"decltype"] = Wide::Lexer::TokenType::Decltype;
        result[L"partial"] = Wide::Lexer::TokenType::Partial;
        result[L"using"] = Wide::Lexer::TokenType::Using;
        result[L"true"] = Wide::Lexer::TokenType::True;
        result[L"false"] = Wide::Lexer::TokenType::False;
        result[L"null"] = Wide::Lexer::TokenType::Null;
        result[L"int"] = Wide::Lexer::TokenType::Int;
        result[L"long"] = Wide::Lexer::TokenType::Long;
        result[L"short"] = Wide::Lexer::TokenType::Short;
        result[L"module"] = Wide::Lexer::TokenType::Module;
        result[L"dynamic"] = Wide::Lexer::TokenType::Dynamic;
        result[L"reinterpret_cast"] = Wide::Lexer::TokenType::ReinterpretCast;
        result[L"static_cast"] = Wide::Lexer::TokenType::StaticCast;
        result[L"enum"] = Wide::Lexer::TokenType::Enum;
        result[L"operator"] = Wide::Lexer::TokenType::Operator;
        result[L"throw"] = Wide::Lexer::TokenType::Throw;
        result[L"public"] = Wide::Lexer::TokenType::Public;
        result[L"private"] = Wide::Lexer::TokenType::Private;
        result[L"protected"] = Wide::Lexer::TokenType::Protected;
        result[L"friend"] = Wide::Lexer::TokenType::Friend;
        result[L"this"] = Wide::Lexer::TokenType::This;

        return result;
    }()
);

std::vector<Wide::Lexer::Token*> Lexer::Context::operator()(Unicode::String* filename, Memory::Arena& arena) {

    Wide::IO::TextInputFileOpenArguments args;
    args.encoding = Wide::IO::Encoding::UTF16;
    args.mode = Wide::IO::OpenMode::OpenExisting;
    args.path = *filename;

    auto str = arena.Allocate<Unicode::String>(args().AsString());
    const wchar_t* begin = str->c_str();
    const wchar_t* end = str->c_str() + str->size();

    int line = 1;
    int column = 1;

    std::vector<Token*> tokens;

    // Some variables we'll need for semantic actions
    Wide::Lexer::TokenType type;

    auto multi_line_comment 
        =  MakeEquality(L'/')
        >> MakeEquality(L'*')
        >> *( !(MakeEquality(L'*') >> MakeEquality(L'/')) >> eps)
        >> eps >> eps;

    auto single_line_comment
        =  MakeEquality(L'/')
        >> MakeEquality(L'/')
        >> *( !MakeEquality(L'\n') >> eps);

    auto punctuation
        =  MakeEquality(L',')[[&]{ type = Wide::Lexer::TokenType::Comma; }]
        || MakeEquality(L';')[[&]{ type = Wide::Lexer::TokenType::Semicolon; }]
        || MakeEquality(L'~')[[&]{ type = Wide::Lexer::TokenType::BinaryNOT; }]
        || MakeEquality(L'(')[[&]{ type = Wide::Lexer::TokenType::OpenBracket; }]
        || MakeEquality(L')')[[&]{ type = Wide::Lexer::TokenType::CloseBracket; }]
        || MakeEquality(L'[')[[&]{ type = Wide::Lexer::TokenType::OpenSquareBracket; }]
        || MakeEquality(L']')[[&]{ type = Wide::Lexer::TokenType::CloseSquareBracket; }]
        || MakeEquality(L'{')[[&]{ type = Wide::Lexer::TokenType::OpenCurlyBracket; }]
        || MakeEquality(L'}')[[&]{ type = Wide::Lexer::TokenType::CloseCurlyBracket; }]

        || MakeEquality(L'>') >> (
               MakeEquality(L'>') >> (
                   MakeEquality(L'=')[[&]{ type = Wide::Lexer::TokenType::RightShiftEquals; }]
                || opt[[&]{ type = Wide::Lexer::TokenType::RightShift; }]) 
            || MakeEquality(L'=')[[&]{ type = Wide::Lexer::TokenType::GreaterThanOrEqualTo; }]
            || opt[[&]{ type = Wide::Lexer::TokenType::GreaterThan; }])
        || MakeEquality(L'<') >> (
               MakeEquality(L'<') >> (
                      MakeEquality(L'=')[[&]{ type = Wide::Lexer::TokenType::LeftShiftEquals; }]
                   || opt[[&]{ type = Wide::Lexer::TokenType::LeftShift; }] ) 
            || MakeEquality(L'=')[[&]{ type = Wide::Lexer::TokenType::LessThanOrEqualTo; }] 
            || opt[[&]{ type = Wide::Lexer::TokenType::LessThan; }])

        || MakeEquality(L'-') >> (
               MakeEquality(L'-')[[&]{ type = Wide::Lexer::TokenType::Decrement; }]
            || MakeEquality(L'=')[[&]{ type = Wide::Lexer::TokenType::MinusEquals; }]
            || MakeEquality(L'>')[[&]{ type = Wide::Lexer::TokenType::PointerAccess; }]
            || opt[[&]{ type = Wide::Lexer::TokenType::Minus; }])

        || MakeEquality(L'.')
            >> (MakeEquality(L'.') >> MakeEquality(L'.')[[&]{ type = Wide::Lexer::TokenType::Ellipsis; }] 
            || opt[[&]{ type = Wide::Lexer::TokenType::Dot; }])

        || MakeEquality(L'+') >> (  
               MakeEquality(L'+')[[&]{ type = Wide::Lexer::TokenType::Increment; }] 
            || MakeEquality(L'=')[[&]{ type = Wide::Lexer::TokenType::PlusEquals; }]
            || opt[[&]{ type = Wide::Lexer::TokenType::Plus; }])
        || MakeEquality(L'&') >> (
               MakeEquality(L'&')[[&]{ type = Wide::Lexer::TokenType::LogicalAnd; }]
            || MakeEquality(L'=')[[&]{ type = Wide::Lexer::TokenType::BinaryANDEquals; }] 
            || opt[[&]{ type = Wide::Lexer::TokenType::BinaryAND; }])
        || MakeEquality(L'|') >> (
               MakeEquality(L'|')[[&]{ type = Wide::Lexer::TokenType::LogicalOr; }]
            || MakeEquality(L'=')[[&]{ type = Wide::Lexer::TokenType::BinaryOREquals; }]
            || opt[[&]{ type = Wide::Lexer::TokenType::BinaryOR; }])

        || MakeEquality(L'*') >> (MakeEquality(L'=')[[&]{ type = Wide::Lexer::TokenType::MulEquals; }] 
            || opt[[&]{ type = Wide::Lexer::TokenType::Multiply; }])
        || MakeEquality(L'%') >> (MakeEquality(L'=')[[&]{ type = Wide::Lexer::TokenType::ModulusEquals; }] 
            || opt[[&]{ type = Wide::Lexer::TokenType::Modulus; }])
        || MakeEquality(L'=') >> (MakeEquality(L'=')[[&]{ type = Wide::Lexer::TokenType::EqualTo; }] 
            || opt[[&]{ type = Wide::Lexer::TokenType::Assignment; }])
        || MakeEquality(L'!') >> (MakeEquality(L'=')[[&]{ type = Wide::Lexer::TokenType::NotEquals; }] 
            || opt[[&]{ type = Wide::Lexer::TokenType::LogicalNOT; }])
        || MakeEquality(L'/') >> (MakeEquality(L'=')[[&]{ type = Wide::Lexer::TokenType::DivEquals; }] 
            || opt[[&]{ type = Wide::Lexer::TokenType::Divide; }])
        || MakeEquality(L'^') >> (MakeEquality(L'=')[[&]{ type = Wide::Lexer::TokenType::BinaryXOREquals; }] 
            || opt[[&]{ type = Wide::Lexer::TokenType::BinaryXOR; }])
        || MakeEquality(L':') >> (MakeEquality(L'=')[[&]{ type = Wide::Lexer::TokenType::VarAssign; }] 
            || opt[[&]{ type = Wide::Lexer::TokenType::Colon; }]);

    auto string
        =  L'"' >> *( L'\\' >> MakeEquality(L'"') >> eps || !MakeEquality(L'"') >> eps) >> eps;

    auto character
        =  L'\'' >> *( L'\\' >> MakeEquality(L'\'') >> eps || !MakeEquality(L'\'') >> eps);

    auto digit
        =  MakeRange(L'0', L'9');

    auto letter
        =  MakeRange(L'a', L'z') || MakeRange(L'A', L'Z');

    auto number
        =  +digit >> ((L'.' >> +digit) || opt);

    auto new_line
        = MakeEquality(L'\n')[ [&] { line++; column = 0; } ];

    auto whitespace
        =  MakeEquality(L' ')
        || L'\t'
        || new_line
        || L'\n'
        || L'\r'
        || multi_line_comment
        || single_line_comment;

    auto identifier 
        =  (letter || L'_') >> *(letter || digit || (L'_'));
        //=  *( !(punctuation || string || character || whitespace) >> eps );

    bool skip = false;

    auto lexer 
        =  whitespace[ [&]{ skip = true; } ] // Do not produce a token for whitespace or comments. Just continue on.
        || punctuation[ [&]{ skip = false; } ] // Type set by individual punctuation
        || string[ [&]{ skip = false; type = Wide::Lexer::TokenType::String; } ]
        || character[ [&]{ skip = false; type = Wide::Lexer::TokenType::Character; } ]
        || number[ [&]{ skip = false; type = Wide::Lexer::TokenType::Number; } ]
        || identifier[ [&]{ skip = false; type = Wide::Lexer::TokenType::Identifier; } ];

    auto current = begin;
    while(current != end) {
        if (!lexer(current, end)) {
            throw std::runtime_error("Failed to lex input.");
        }
        column += (current - begin);
        if (skip) {
            begin = current;
            continue;
        }
        Token t(begin, current);
        t.columnbegin = column - (current - begin);
        t.columnend = column;
        t.file = filename;
        t.line = line;
        if (type == Wide::Lexer::TokenType::Identifier) { // check for reserved word
            if (reserved_words.find(t.Codepoints()) != reserved_words.end())
                t.type = reserved_words.find(t.Codepoints())->second;
            else
                t.type = Wide::Lexer::TokenType::Identifier;
        } else {
            t.type = type;
        }
        begin = current;
        tokens.push_back(arena.Allocate<Token>(t));
    }
    return tokens;
}

এটি একটি পুনরাবৃত্ত-ভিত্তিক, ব্যাক-ট্র্যাকিং, সসীম স্টেট মেশিন লাইব্রেরি দ্বারা সমর্থনযুক্ত যা দৈর্ঘ্যে ~ 400 লাইন। যাইহোক, এটা দেখতে আমি কনস্ট্রাক্ট সহজ বুলিয়ান অপারেশন, মত ছিল কি ছিল সহজ and, orএবং not, এবং Regex ধাঁচের অপারেটার দুয়েক মত *শূন্য-অর-বেশি, জন্য epsমানে "ম্যাচে কিছু" এবং optমানে "ম্যাচ কিছু কিন্তু এটি গ্রাস করবেন না "। গ্রন্থাগারটি সম্পূর্ণ জেনেরিক এবং পুনরাবৃত্তকারীদের উপর ভিত্তি করে। MakeEquality স্টাফ মধ্যে *itমান এবং পাস মান মধ্যে সমতা জন্য একটি সহজ পরীক্ষা এবং MakeRange একটি সহজ<= >= পরীক্ষা পরীক্ষা।

শেষ পর্যন্ত, আমি ব্যাকট্র্যাকিং থেকে ভবিষ্যদ্বাণীতে চলে যাওয়ার পরিকল্পনা করছি।


2
আমি বেশ কয়েকটি লেক্সার দেখেছি যা পার্সার দ্বারা অনুরোধ করার সময় পরবর্তী টোকেনটি পড়েছিল read আপনার মনে হয় পুরো ফাইলটি দিয়ে টোকেনের একটি তালিকা তৈরি করবেন। এই পদ্ধতিতে কি কোনও বিশেষ সুবিধা রয়েছে?
ব্যবহারকারী 673679

2
@ ডেডএমজি: MakeEqualityস্নিপেট ভাগ করে নেওয়া যত্ন ? বিশেষত এই ফাংশনটি দিয়ে বস্তুটি ফিরে এসেছে। দেখতে খুব ইন্টারেস্টিং লাগছে।
ডেথিকন

3

প্রথমত, এখানে বিভিন্ন জিনিস চলছে:

  • খালি অক্ষরের তালিকাকে টোকেনে বিভক্ত করা
  • এই টোকেনগুলি সনাক্ত করা (কীওয়ার্ডগুলি, আক্ষরিক, বন্ধনীগুলি চিহ্নিত করা ...)
  • একটি সাধারণ ব্যাকরণ কাঠামো যাচাই করা

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

আমার জানা সবচেয়ে আশ্চর্যজনক লেসারটি হ'ল বুস্ট.স্পিরিট.কিউ । এটি আপনার ল্যাক্সার এক্সপ্রেশনগুলি তৈরি করতে এক্সপ্রেশন টেম্পলেটগুলি ব্যবহার করে এবং একবার এর বাক্য গঠনতে অভ্যস্ত হয়ে কোডটি সত্যিই ঝরঝরে অনুভব করে। এটি (ভারী টেমপ্লেট) যদিও খুব ধীরে ধীরে সংকলন করে, তাই স্পর্শ না করা অবস্থায় তাদের পুনরায় সংশোধন করা এড়াতে উত্সর্গীকৃত ফাইলগুলির বিভিন্ন অংশকে আলাদা করা ভাল best

পারফরম্যান্সে কিছু অসুবিধা রয়েছে এবং এপিচ সংকলকের লেখক ব্যাখ্যা করেছেন যে কীভাবে নিবন্ধে কিউ কাজ করে সে সম্পর্কে নিবিড় প্রোফাইলিং এবং তদন্ত করে তিনি কীভাবে 1000x স্পিড-আপ পেয়েছিলেন ।

অবশেষে, বাহ্যিক সরঞ্জামগুলির দ্বারাও উত্পন্ন কোড রয়েছে (ইয়্যাক, বাইসন, ...)।


তবে আমি ব্যাকরণ যাচাইকরণটি স্বয়ংক্রিয় করার ক্ষেত্রে কী ভুল ছিল সে বিষয়ে একটি লিখিত প্রতিশ্রুতি দিয়েছিলাম।

উদাহরণস্বরূপ, আপনি কলং পরীক্ষা করে দেখুন, আপনি বুঝতে পারবেন যে উত্পন্ন পার্সার এবং বুস্ট.স্প্রিট এর মতো কিছু ব্যবহার করার পরিবর্তে তারা জেনেরিক বংশোদ্ভূত পার্সিং প্রযুক্তি ব্যবহার করে ব্যাকরণটি ম্যানুয়ালি বৈধ করার জন্য প্রস্তুত হয়েছিল। এটা কি পিছনে বলে মনে হচ্ছে?

আসলে, একটি খুব সহজ কারণ আছে: ত্রুটি পুনরুদ্ধার

সাধারণ উদাহরণ, সি ++ তে:

struct Immediate { } instanceOfImmediate;

struct Foo {}

void bar() {
}

ত্রুটি লক্ষ্য করুন? ঘোষণার ঠিক পরে নিখোঁজ আধা-কোলন Foo

এটি একটি সাধারণ ত্রুটি, এবং কলং এটি অনুপস্থিত যে বুঝতে পেরে খুব সুন্দরভাবে পুনরুদ্ধার করে এবং voidএটি উদাহরণ হিসাবে Fooনয় তবে পরবর্তী ঘোষণার অংশ। এটি ক্রিপ্টিক ত্রুটি বার্তাগুলি নির্ণয় করা কঠিন এড়ায়।

বেশিরভাগ স্বয়ংক্রিয় সরঞ্জামগুলির মধ্যে সেই সম্ভাব্য ভুলগুলি এবং সেগুলি থেকে কীভাবে পুনরুদ্ধার করা যায় তা নির্দিষ্ট করার কোনও উপায় নেই (অন্তত সুস্পষ্ট)। প্রায়শই পুনরুদ্ধার করতে একটু সিনট্যাকটিক বিশ্লেষণের প্রয়োজন হয় তাই এটি সুস্পষ্ট।


সুতরাং, অটোমেটেড সরঞ্জাম ব্যবহারে বাণিজ্য-জড়িত রয়েছে: আপনি আপনার পার্সার দ্রুত পান তবে এটি ব্যবহারকারী-বান্ধব কম।


3

যেহেতু আপনি লেক্সারগুলি কীভাবে কাজ করে তা শিখতে চান, আমি অনুমান করি আপনি প্রকৃতপক্ষে লেক্সার জেনারেটর কীভাবে কাজ করেন তা জানতে চান।

একটি লেক্সার জেনারেটর একটি লেজিকাল স্পেসিফিকেশন নেয় যা নিয়মের তালিকা (নিয়মিত-এক্সপ্রেশন-টোকেন জোড়) এবং একটি লেক্সার জেনারেট করে। এই ফলস্বরূপ লেক্সার তারপরে নিয়মের এই তালিকা অনুযায়ী কোনও ইনপুট (অক্ষর) স্ট্রিংকে টোকেন স্ট্রিংয়ে রূপান্তর করতে পারে।

যে পদ্ধতিটি সর্বাধিক ব্যবহৃত হয় তা হ'ল একটি নিয়মিত ভাবকে একটি ডিস্ট্রিনিস্টিক সসীম অটোমেটা (ডিএফএ) এ ননডেটারিস্টিনিস্টিক অটোমেটা (এনএফএ) এর মাধ্যমে আরও কিছু বিবরণ রূপান্তর করে।

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

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

আরও কয়েকটি জিনিস রয়েছে যা সাধারণত লেক্সারে নিযুক্ত হয় না বা পাঠ্যে চিকিত্সা করা হয় না তবে তবুও এটি বেশ কার্যকর:

প্রথমত, ইউনিকোড পরিচালনা করা কিছুটা অনানুষ্ঠানিক। সমস্যাটি হ'ল ASCII ইনপুটটি কেবল 8 টি বিট প্রশস্ত, যার অর্থ আপনি সহজেই ডিএফএ-র প্রতিটি রাজ্যের জন্য একটি রূপান্তর টেবিল রাখতে পারেন কারণ তাদের কেবল 256 টি প্রবেশ রয়েছে। তবে, ইউনিকোড, 16 বিট প্রস্থে (আপনি যদি ইউটিএফ -16 ব্যবহার করেন), ডিএফএতে প্রতিটি প্রবেশের জন্য k৪ কে টেবিলের প্রয়োজন। আপনার যদি জটিল ব্যাকরণ থাকে, তবে এটি বেশ কিছু জায়গা নেওয়া শুরু করতে পারে। এই টেবিলগুলি পূরণ করতে বেশ খানিকটা সময় নেওয়া শুরু হয়।

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

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

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

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.