parsing सरल पार्सर का विकास करना




bison flex-lexer (8)

अगर आपकी चाहत सी # के अनुसार इस प्रश्न के मुताबिक गार्डन प्वाइंट जीपीपीजी और जीपीएलएक्स

मेरे दिन की नौकरी में पास्कल जैसे संकलक को विकसित करने में काम करना शामिल है मैं अनुकूलन और कोड पीढ़ी के साथ काम कर रहा हूं।

मैं भी एक ही भाषा के लिए एक साधारण पार्सर बनाने के लिए सीखना शुरू करना चाहूंगा। मैं वास्तव में यह नहीं जानता कि कैसे इस बारे में जाना है फ्लेक्स और बाइसन को पसंद लगता है लेकिन, सी ++ या सी # का इस्तेमाल करने वाला पार्सर लिखना संभव नहीं है? मैं सी के साथ थोड़ा डरावना हूँ

Yacc ++ सी # का समर्थन करता है, लेकिन यह लाइसेंस प्राप्त एक है मैं इस संबंध में सभी मदद की तलाश कर रहा हूं। सुझावों की अत्यधिक सराहना की जाएगी


अगर आप इसे जावा में लिख रहे थे, तो मैं एएनटीएलआर की सिफारिश करता था। यह जावा में लिखा गया एक अच्छा एलएल (*) पार्सर जनरेटर है अमेज़ॅन पर इसके लिए एक भयानक किताब भी है,


आप वास्तव में C ++ के साथ फ्लेक्स और बायसन का उपयोग कर सकते हैं इस ट्यूटोरियल में , उदाहरण के लिए, आप देख सकते हैं कि धारा 5 उस बात को समर्पित है। बस इसके लिए गूगल, और मुझे यकीन है कि आप बहुत सारे उदाहरण मिल जाएगा


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

जिसके परिणामस्वरूप पार्सर और लेसर का सी इंटरफेस है, यह सच है। हालांकि सी ++ सहित अधिकांश भाषाओं में सी (या रैप) सी इंटरफेस कॉल करने के लिए सरल तरीके हैं।

मैं इसमें एक विशेषज्ञ नहीं हूं, लेकिन मुझे यकीन है कि उपरोक्त सभी एएनटीएलआर के लिए भी जाता है।

यदि आप इसे "शुद्ध सी ++" (जो कुछ भी इसका मतलब है) में करने को कह रहे हैं, तो बूस्ट की भावना का उपयोग करें। यदि सैद्धांतिक शुद्धता में मैं वास्तव में इस बिंदु को नहीं देखता हूं, तो यह अधिक से अधिक काम का कारण बनता है।

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


मेरा मानना ​​है कि आप सी # के साथ एएनटीएलआर का उपयोग कर सकते हैं। मैंने इसे स्वयं (कभी भी) की कोशिश नहीं की है, हालांकि यहां एक ट्यूटोरियल है जो आपको सही दिशा में बता सकता है।


निजी तौर पर, मैं अपना अपना लेक्सर और पार्सर रोल करता हूं (एलएल) यहाँ एक बहुत संक्षिप्त उदाहरण है यह सी ++ में है, लेकिन उम्मीद है कि आप इसे अनुकूलित कर सकते हैं। यह एक मैक्रो PARSE_HIGHER का उपयोग करता है ताकि ऑपरेटर को अलग-अलग स्तर पर बदलना आसान न हो।

 // routine to scan over whitespace/comments
void ScanWhite(const char* &pc){
  while(true){
    if(0);
    else if (WHITESPACE(*pc)) pc++;
    else if (pc[0]=='/' && pc[1]=='/'){
      while(*pc && *pc++ != '\n');
    }
    else break;
  }
}
// routine to lex an identifier
bool SeeId(const char* &pc, string sId){
  ScanWhite(pc);
  const char* pc0 = pc;
  if (alpha(*pc)){
    sId = "";
    while(alphanum(*pc)) sId += (*pc++);
    return true;
  }
  pc = pc0;
  return false;
}
// routine to lex a number
bool SeeNum(const char* &pc, double &dNum){
  ScanWhite(pc);
  const char* pc0 = pc;
  if (digit(*pc)){
    dNum = 0;
    while(digit(*pc)) dNum = dNum * 10 + (*pc++ - '0');
    if (*pc == '.'){
      double divisor = 1, frac = 0;
      while(digit(*pc)){
        divisor *= 0.1;
        frac += (*pc++ - '0') * divisor;
      }
      dNum += frac;
    }
    return true;
  }
  pc = pc0;
  return false;
}
// routine to lex some constant word
bool SeeWord(const char* &pc, const char* sWord){
  ScanWhite(pc);
  const char* pc0 = pc;
  int len = strlen(sWord);
  if (strncmp(pc, sWord, len)==0 && !alphanum(pc[len])){
    pc += len;
    return true;
  }
  pc = pc0;
  return false;
}
// routine to lex a single character like an operator
bool SeeChar(const char* &pc, const char c){
  ScanWhite(pc);
  const char* pc0 = pc;
  if (*pc == c){
    pc++;
    return true;
  }
  pc = pc0;
  return false;
}
// primitive expression parser
void ParsePrimitiveExpr(const char* &pc, CNode* &p){
  double dNum;
  char sId[100];
  if (0);
  else if (SeeNum(pc, dNum)){
    p = new CNode(dNum);
  }
  else if (SeeId(pc, sId)){
    // see if its a function call
    if (SeeChar(pc, '(')){
      p = MakeNewFunctionCallNode(sId);
      while(!SeeChar(pc, ')')){
        CNode* p1 = null;
        ParseExpression(pc, p1);
        AddArgumentExprToFunctionCallNode(p, p1);
        SeeChar(pc, ','); /* optional comma separator */
      }
    }
    // otherwise its just a variable reference
    else {
      p = new CNode(sId);
    }
  }
  // handle embedded expressions
  else if (SeeChar(pc, '(')){
    ParseExpression(pc, p);
    if (!SeeChar(pc, ')')) /* deal with syntax error */
  }
}
#define PARSE_HIGHER ParsePrimitiveExpr
// product parser
void ParseProduct(const char* &pc, CNode* &p){
  PARSE_HIGHER(pc, p);
  while(true){
    if (0);
    else if (SeeChar(pc, '*')){
      CNode p1 = null;
      PARSE_HIGHER(pc, p1);
      p = new CNode('*', p, p1);
    }
    else if (SeeChar(pc, '/')){
     CNode p1 = null;
     PARSE_HIGHER(pc, p1);
     p = new CNode('/', p, p1);
   }
   else break;
  }
}
#undef  PARSE_HIGHER
#define PARSE_HIGHER ParseProduct
// sum parser
void ParseSum(const char* &pc, CNode* &p){
  PARSE_HIGHER(pc, p);
  while(true){
    if (0);
    else if (SeeChar(pc, '+')){
      CNode p1 = null;
      PARSE_HIGHER(pc, p1);
      p = new CNode('+', p, p1);
    }
    else if (SeeChar(pc, '-')){
      CNode p1 = null;
      PARSE_HIGHER(pc, p1);
      p = new CNode('-', p, p1);
    }
   else break;
  }
}
#undef  PARSE_HIGHER
// can insert more routines like the above
// to handle move operators
#define PARSE_HIGHER ParseSum
// overall expression parser
void ParseExpression(const char* &pc, CNode* &p){
  PARSE_HIGHER(pc, p);
}

कुछ पास्कल शैली के वाक्यविन्यास वाक्यविन्यास जोड़े गए:

void ParseStatement(const char* &pc){
  char sId[100];
  if(0);
  else if (SeeWord(pc, "begin")){
    while(!SeeWord(pc, "end")){
      ParseStatement(pc);
      SeeChar(pc, ';');
    }
  }
  else if (SeeWord(pc, "while")){
    CNode* p1 = null;
    ParseExpression(pc, p1);
    ParseStatement(pc);
    /* semantics for while statement */
  }
  else if (SeeWord(pc, "if")){
    CNode* p1 = null;
    ParseExpression(pc, p1);
    ParseStatement(pc);
    if (SeeWord(pc, "else")){
      ParseStatement(pc);
    }
    /* semantics for if statement */
  }
  else if (SeeWord(pc, "for")){
    /* you do it */
  }
  // handle assignments and subroutine calls
  else if (SeeId(pc, sId)){
    if(0);
    else if (SeeChar(pc, '=')){
      CNode* p1 = null;
      ParseExpression(pc, p1);
      /* semantics for assignment statement */
    }
    else if (SeeChar(pc, '(')){
      CNode* p = MakeNewFunctionCallNode(sId);
      while(!SeeChar(pc, ')')){
        CNode* p1 = null;
        ParseExpression(pc, p1);
        AddArgumentExprToFunctionCallNode(p, p1);
        SeeChar(pc, ','); /* optional comma separator */
      }
    }
    else {
      /* we have a 1-word statement, which is OK in pascal */
    }
  }
  else {
    /* syntax error */
  }
}

यह अभी भी सरणी अनुक्रमणिका, चर घोषणा, और फ़ंक्शन परिभाषा के लिए सिंटैक्स की आवश्यकता है, लेकिन मुझे आशा है कि यह स्पष्ट है कि यह कैसे करना है।


अपने क्लासिक प्रोग्रामिंग पाठ में, एल्गोरिदम + डाटा स्ट्रक्चर्स = प्रोग्राम , निकलॉस विर्थ एक साधारण पुनरावर्ती वंश पार्सल (पास्कल में) को सरल पास्कल 0 जैसी भाषा के लिए विकसित करता है।


मैंने फ्लेक्स और बीज़न के साथ एक्सएसएलटी पार्सर लिखा है। और हाल ही में मैं एएनटीएलआर का उपयोग कर एक परियोजना कर रहा हूं, हालांकि:

क्या जेफिग भाषा वाक्यविन्यास कुशल और स्पष्ट है (और स्प्रिंग-फ्रेमवर्क के एक्सएमएल डीएसएल से बेहतर है)?

मुझे फ्लेक्स और बाइसन की तुलना में बहुत अधिक एएनटीएलआर में काम करना पसंद आया। एएनटीएलआर आपको कुछ मामलों में अमूर्त के उच्च स्तर पर रखता है। लेक्सिकल परिभाषाएं और पार्सर व्याकरण सभी एक फाइल में जा सकते हैं (ANTLR टोकन फ़ाइल उत्पन्न करेगा।)

मुख्य वस्तुओं में से एक पेड़ व्याकरण को परिभाषित करने की क्षमता है असल में आप इनपुट भाषा पर एक व्याकरण का पार्स करते हैं और ऐसी क्रियाएं होती हैं जो उच्चतम एस्ट ट्री आउटपुट (जो स्मृति में जुड़े डेटा संरचना के रूप में रहती हैं) को फिर से लिखती हैं। फिर आप इस पेड़ को एक अलग पेड़ पार्सर फ़ाइल में परिभाषित दूसरे पार्स को पारित कर सकते हैं। पेड़ पार्सर है, जहां आप चाहते हैं कि आप जिस एक्शन मदों का असली काम करते हैं

यह एक अच्छा तरीका है क्योंकि आप एएसटी फॉर्म को रख सकते हैं और बार-बार पुनर्रचना कर सकते हैं - बाद में क्रियाओं, आदि के आधार पर प्रक्रिया करने के लिए विशिष्ट उप-नोड्स को छीलने के लिए। भाषा व्याख्याता की तरह कुछ सोचें। लूप में जाने के बजाय और फिर से जमीन से भाषा को प्रोसेस करने के बजाय, केवल एएसटी प्रतिनिधित्व के माध्यम से इसकी प्रक्रिया कर सकते हैं।

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

अंत में, मेरा वर्तमान बीन कारखाना जावा को लक्षित कर रहा है, लेकिन मैं ActionScript3 और C # .NET को लक्षित करना चाहता हूं। एएनटीएलआर को ऐसा करने के लिए समर्थन है

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





flex-lexer