सी ++ में एक लेसर लिखना


18

सी ++ (किताबें, ट्यूटोरियल, दस्तावेज) में एक लेक्सर लिखने के लिए अच्छे संसाधन क्या हैं, कुछ अच्छी तकनीक और अभ्यास क्या हैं?

मैंने इंटरनेट पर देखा है और हर कोई lexer जैसे lexer जनरेटर का उपयोग करने के लिए कहता है। मैं ऐसा नहीं करना चाहता, मैं हाथ से एक लेख लिखना चाहता हूं।


ठीक है, लेक्स आपके उद्देश्य के लिए अच्छा क्यों नहीं है?
कार्नेकोड

13
मैं सीखना चाहता हूं कि लेक्सर्स कैसे काम करते हैं। मैं ऐसा नहीं कर सकता कि एक लेक्सर जनरेटर के साथ।
राइटफोल्ड

11
लेक्स घृणित सी कोड उत्पन्न करता है। जो भी एक अच्छा लेक्सर चाहता है वह लेक्स का उपयोग नहीं करता है।
डेडजैम

5
@ जियोर्जियो: उत्पन्न कोड वह कोड होता है, जिसे आपको गैर-थ्रेड-सुरक्षित ग्लोबल वैरिएबल के साथ इंटरफेस करना होता है, उदाहरण के लिए, और यह वह कोड है, जिसके नेल-टर्मिनेशन बग आप अपने आवेदन में प्रस्तुत कर रहे हैं।
डेडजैम

1
@ जियोर्जियो: क्या आपको कभी लेक्स द्वारा कोड आउटपुट डिबग करना पड़ा है?
मटनज

जवाबों:


7

ध्यान रखें कि प्रत्येक परिमित राज्य मशीन एक नियमित अभिव्यक्ति से मेल खाती है, जो एक संरचित कार्यक्रम से मेल खाती है ifऔर उपयोग करती है while

इसलिए, उदाहरण के लिए, पूर्णांक को पहचानने के लिए आपके पास राज्य मशीन हो सकती है:

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

या नियमित अभिव्यक्ति:

digit digit*

या संरचित कोड:

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

व्यक्तिगत रूप से, मैं हमेशा उत्तरार्द्ध का उपयोग करते हुए लेक्सर्स लिखता हूं, क्योंकि IMHO यह कम स्पष्ट नहीं है, और तेजी से कुछ भी नहीं है।


मुझे लगता है कि अगर नियमित अभिव्यक्ति बहुत जटिल हो जाती है, तो इसी कोड है। यही कारण है कि लेक्सर जनरेटर अच्छा है: मैं आमतौर पर केवल एक लेक्सेर को खुद कोड करूंगा यदि भाषा बहुत सरल है।
जियोर्जियो

1
@ जियोर्जियो: शायद यह स्वाद की बात है, लेकिन मैंने इस तरह कई पार्सर्स बनाए हैं। संख्या, विराम चिह्न, कीवर्ड, पहचानकर्ता, स्ट्रिंग स्थिरांक, व्हाट्सएप, और टिप्पणियों से परे लेसर को कुछ भी संभालने की आवश्यकता नहीं है।
माइक डनलैवी

मैंने कभी एक जटिल पार्सर नहीं लिखा है और मेरे द्वारा लिखे गए सभी लेक्सर्स और पार्सर्स को भी हाथ से कोडित किया गया था। मुझे आश्चर्य है कि कैसे अधिक जटिल नियमित भाषाओं के लिए यह तराजू है: मैंने कभी इसकी कोशिश नहीं की है लेकिन मुझे लगता है कि एक जनरेटर (जैसे lex) का उपयोग करना अधिक कॉम्पैक्ट होगा। मैं मानता हूं कि कुछ खिलौना उदाहरणों से परे मुझे लेक्स या अन्य जनरेटर के साथ कोई अनुभव नहीं है।
जियोर्जियो

1
वहाँ एक स्ट्रिंग तुम *pcकरने के लिए अपील करेंगे, है ना? की तरह while(isdigit(*pc)) { value += pc; pc++; }। उसके बाद जब }आप मान को एक संख्या में परिवर्तित करते हैं और उसे एक टोकन को असाइन करते हैं।
15

@WTP: संख्याओं के लिए, मैं बस उन्हें मक्खी पर, के समान गणना करता हूं n = n * 10 + (*pc++ - '0');। यह फ्लोटिंग पॉइंट और 'ई' नोटेशन के लिए थोड़ा और जटिल हो जाता है, लेकिन खराब नहीं। मुझे यकीन है कि मैं पात्रों को बफर में पैक करके और कॉल करके atofया जो भी हो, थोड़ा कोड सहेज सकता हूं । यह किसी भी तेजी से नहीं चलेगा।
माइक डनलैवी

9

लेक्सर्स परिमित राज्य मशीनें हैं। इसलिए, उनका निर्माण किसी भी सामान्य प्रयोजन वाले FSM पुस्तकालय द्वारा किया जा सकता है। अपनी खुद की शिक्षा के प्रयोजनों के लिए, हालांकि, मैंने अभिव्यक्ति टेम्पलेट का उपयोग करके अपना स्वयं का लिखा। यहाँ मेरा लेसर है:

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, और रेगेक्स-शैली के कुछ ऑपरेटरों को जैसे *शून्य या अधिक के लिए, eps"मैच कुछ भी" और optमतलब "मैच" के लिए। कुछ भी नहीं लेकिन यह उपभोग नहीं करते "। पुस्तकालय पूरी तरह से सामान्य है और पुनरावृत्तियों पर आधारित है। MakeEquality सामान के बीच समानता के लिए एक साधारण परीक्षण है *itऔर मूल्य में उत्तीर्ण है, और MakeRange एक सरल <= >=परीक्षण है।

आखिरकार, मैं बैकट्रैकिंग से भविष्यवाणियों की ओर बढ़ने की योजना बना रहा हूं।


2
मैंने कई लेक्सर्स देखे हैं जो ऐसा करने के लिए पार्सर द्वारा अनुरोध किए जाने पर बस अगले टोकन को पढ़ते हैं। तुम्हारा एक पूरी फ़ाइल के माध्यम से जाने के लिए और टोकन की एक सूची बनाने के लिए लगता है। क्या इस पद्धति का कोई विशेष लाभ है?
user673679 12

2
@DeadMG: MakeEqualityस्निपेट साझा करने की देखभाल ? विशेष रूप से ऑब्जेक्ट उस फ़ंक्शन द्वारा लौटाया गया है। बहुत दिलचस्प लग रहा है।
मृत्युंजय

3

सबसे पहले, यहां अलग-अलग चीजें चल रही हैं:

  • नंगे पात्रों की सूची को टोकन में विभाजित करना
  • उन टोकन को पहचानना (कीवर्ड, शाब्दिक, कोष्ठक, ... पहचानना)
  • एक सामान्य व्याकरण संरचना की पुष्टि करना

आम तौर पर, हम एक लेक्सर से एक ही बार में सभी 3 चरणों को करने की अपेक्षा करते हैं, हालांकि उत्तरार्द्ध स्वाभाविक रूप से अधिक कठिन है और स्वचालन के साथ कुछ मुद्दे हैं (इस पर बाद में अधिक)।

मुझे पता है कि सबसे आश्चर्यजनक lexer Boost.Spirit.Qi है । यह आपके लेक्सर भाव उत्पन्न करने के लिए अभिव्यक्ति टेम्पलेट्स का उपयोग करता है, और एक बार इसके सिंटैक्स के आदी होने पर कोड वास्तव में साफ-सुथरा लगता है। हालांकि यह बहुत धीरे-धीरे संकलित होता है (भारी टेम्पलेट), इसलिए जब उन्हें छुआ नहीं गया है तो उन्हें पुन: जमा करने से बचने के लिए समर्पित फाइलों में विभिन्न भागों को अलग करना सबसे अच्छा है।

प्रदर्शन में कुछ कमियां हैं, और एपोच कंपाइलर के लेखक बताते हैं कि एक लेख में क्यूई कैसे काम करता है , इसकी गहन रूपरेखा और जांच के द्वारा उन्हें एक 1000x स्पीड-अप मिला ।

अंत में, बाहरी उपकरण (Yacc, Bison, ...) द्वारा उत्पन्न कोड भी होते हैं।


लेकिन मैंने व्याकरण सत्यापन को स्वचालित करने में क्या गलत था, इस पर लिखने का वादा किया।

यदि आप क्लैंग की जांच करते हैं, उदाहरण के लिए, आप महसूस करेंगे कि एक उत्पन्न पार्सर और बूस्ट.पिरिट जैसी किसी चीज़ का उपयोग करने के बजाय, वे एक सामान्य डिसेंट पार्सिंग टेक्निक का उपयोग करके व्याकरण को मैन्युअल रूप से मान्य करने के लिए निर्धारित करते हैं। निश्चित रूप से यह पिछड़ा हुआ लगता है?

वास्तव में, एक बहुत ही सरल कारण है: त्रुटि पुनर्प्राप्ति

C ++ में विशिष्ट उदाहरण:

struct Immediate { } instanceOfImmediate;

struct Foo {}

void bar() {
}

त्रुटि सूचना दें? की घोषणा के ठीक बाद एक लापता अर्ध-उपनिवेश Foo

यह एक सामान्य त्रुटि है, और क्लैंग को यह समझकर बड़े करीने से पता चलता है कि यह बस गायब है और अगले घोषणा के भाग का voidउदाहरण नहीं है Foo। यह गुप्त त्रुटि संदेशों का निदान करने के लिए कड़ी मेहनत से बचता है।

अधिकांश स्वचालित टूल में उन संभावित गलतियों को निर्दिष्ट करने और उनसे पुनर्प्राप्त करने के तरीके पर कोई (कम से कम स्पष्ट) तरीके नहीं हैं। अक्सर ठीक होने के लिए थोड़े से वाक्यात्मक विश्लेषण की आवश्यकता होती है इसलिए यह स्पष्ट है।


तो, एक स्वचालित उपकरण का उपयोग करने में व्यापार-बंद शामिल है: आप अपने पार्सर को जल्दी से प्राप्त करते हैं, लेकिन यह कम उपयोगकर्ता के अनुकूल है।


3

चूंकि आप सीखना चाहते हैं कि कैसे लेक्सर्स काम करते हैं, तो मुझे लगता है कि आप वास्तव में जानना चाहते हैं कि कैसे लेक्सर जनरेटर काम करते हैं।

एक lexer जनरेटर एक lexical विनिर्देश लेता है, जो नियमों (नियमित-अभिव्यक्ति-टोकन जोड़े) की एक सूची है, और एक lexer उत्पन्न करता है। इसके परिणामस्वरूप लेक्सर इस नियम के अनुसार एक इनपुट (वर्ण) स्ट्रिंग को टोकन स्ट्रिंग में बदल सकता है।

जिस विधि का आमतौर पर सबसे अधिक उपयोग किया जाता है, उसमें एक नियमित अभिव्यक्ति को एक नियत परिमित ऑटोमेटा (DFA) में एक nondeterministic automata (NFA) के माध्यम से परिवर्तित किया जाता है, साथ ही कुछ विवरण भी।

इस परिवर्तन को करने का एक विस्तृत मार्गदर्शिका यहाँ पाया जा सकता है । ध्यान दें कि मैंने इसे स्वयं नहीं पढ़ा है, लेकिन यह काफी अच्छा है। इसके अलावा, कंपाइलर निर्माण पर किसी भी पुस्तक के बारे में पहले कुछ अध्यायों में यह परिवर्तन होगा।

यदि आप विषय पर पाठ्यक्रमों के व्याख्यान स्लाइड में रुचि रखते हैं, तो संकलक निर्माण पर पाठ्यक्रम से उनमें से कोई भी अंतहीन संदेह नहीं है। मेरे विश्वविद्यालय से, आप यहां और यहां ऐसी स्लाइड पा सकते हैं ।

कुछ और चीजें हैं जो आमतौर पर लेक्सर्स में नियोजित नहीं हैं या ग्रंथों में इलाज की जाती हैं, लेकिन फिर भी काफी उपयोगी हैं:

सबसे पहले, यूनिकोड को संभालना कुछ हद तक nontrivial है। समस्या यह है कि एएससीआईआई इनपुट केवल 8 बिट्स चौड़ा है, जिसका अर्थ है कि आप आसानी से डीएफए में हर राज्य के लिए एक संक्रमण तालिका रख सकते हैं, क्योंकि उनके पास केवल 256 प्रविष्टियां हैं। हालाँकि, यूनिकोड, 16 बिट्स चौड़ा (यदि आप UTF-16 का उपयोग करते हैं), तो DFA में प्रत्येक प्रविष्टि के लिए 64k तालिकाओं की आवश्यकता होती है। यदि आपके पास जटिल व्याकरण है, तो यह काफी कुछ जगह लेना शुरू कर सकता है। इन तालिकाओं को भरने में भी काफी समय लगने लगता है।

वैकल्पिक रूप से, आप अंतराल के पेड़ उत्पन्न कर सकते हैं। एक श्रेणी के पेड़ में टुपल्स ('ए', 'जेड'), ('ए', 'जेड') उदाहरण के लिए हो सकते हैं, जो कि पूर्ण तालिका होने की तुलना में बहुत अधिक मेमोरी कुशल है। यदि आप गैर-अतिव्यापी अंतराल बनाए रखते हैं, तो आप इस उद्देश्य के लिए किसी भी संतुलित बाइनरी ट्री का उपयोग कर सकते हैं। चलने का समय बिट्स की संख्या में हर वर्ण के लिए रैखिक होता है, इसलिए यूनिकोड मामले में हे (16)। हालांकि, सबसे अच्छे मामले में, यह आमतौर पर थोड़ा कम होगा।

एक और मुद्दा यह है कि आमतौर पर उत्पन्न होने वाले लेक्सर्स में सबसे खराब स्थिति होती है। हालांकि यह सबसे खराब स्थिति वाला व्यवहार आमतौर पर नहीं देखा जाता है, यह आपको काट सकता है। यदि आप समस्या में भाग लेते हैं और इसे हल करना चाहते हैं, तो यह बताने के लिए कि रैखिक समय कैसे प्राप्त किया जा सकता है, एक कागज यहां पाया जा सकता है

आप शायद स्ट्रिंग रूप में नियमित अभिव्यक्तियों का वर्णन करने में सक्षम होना चाहते हैं, क्योंकि वे सामान्य रूप से दिखाई देते हैं। हालांकि, एनएफए (या संभवतः एक पुनरावर्ती मध्यवर्ती संरचना) में इन नियमित अभिव्यक्ति विवरणों को पार्स करना चिकन-अंडे की समस्या का एक सा है। नियमित अभिव्यक्ति विवरण को पार्स करने के लिए, शंटिंग यार्ड एल्गोरिथ्म बहुत उपयुक्त है। लगता है कि विकिपीडिया एल्गोरिथम पर एक व्यापक पृष्ठ है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.