سؤال

وظيفتي النهارية تشمل العمل على تطوير مترجم شبيه بزمal. لقد كنت أعمل كل شيء على تحسينات وتوليد التعليمات البرمجية.

أود أيضا أن أبدأ في تعلم بناء محلل بسيط لنفس اللغة. ومع ذلك، ليس من المؤكد حقا كيف أذهب حول هذا الأمر. فليكس و bison يبدو أن الاختيار. ولكن، أليس من الممكن كتابة محلل باستخدام C ++ أو C #؟ أنا زاحف قليلا مع C.

YACC ++ يدعم C #، ولكنها واحدة مرخصة. أبحث عن كل المساعدة التي يمكنني العثور عليها في هذا الصدد. سيكون موضع تقدير كبير في الاقتراحات.

هل كانت مفيدة؟

المحلول

أعتقد أنه يمكنك استخدام Antlr مع C #. لم أحاول أبدا نفسي (حتى الآن)، ولكن هناك تعليمي تعليمي هنا قد يشير لك في الاتجاه الصحيح.

نصائح أخرى

شخصيا، أنا أرفق بلدي Lexer الخاص بي ومحلل (ليرة لبنانية). إليك مثال مختصر للغاية. إنه في C ++، ولكن نأمل أن تتمكن من تكييفها. يجعل استخدام 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);
}

وأضاف بعض بناء جملة بيان أسلوب Pascal:

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 */
  }
}

لا يزال يحتاج إلى بناء جملة لمفهرسة الصفيف وإعلان متغير وتعريف الوظيفة، لكنني آمل أن يكون من الواضح كيفية القيام بذلك.

في نص البرمجة الكلاسيكية، خوارزميات + هياكل البيانات = البرامج, ، تتطور نيكلاوس ويرث محلل نزول متكرر كامل (باكال) للحصول على لغة بسيطة تشبه Pascal0.

إذا كنت تكتبها في جافا، فسوف أوصي Antlr. انها لطيفة (*) محلل محلل مكتوب في جافا. هناك كتاب رائع له على الأمازون أيضا.

Bison & Flex هي مولدات المحللين الكنسيين. إذا كنت مهتما في C ++، فقد وجدت تعزيز الروح مفيد. لم أستخدمها أبدا لأي شيء معقد كمجموع. أنا متأكد من أن الآخرين سيكون لديهم اقتراحات مثيرة للاهتمام لغات أخرى مثل C # ...

يمكنك بالفعل استخدام Flex & Bison مع C ++. في هذا البرنامج التعليمي, على سبيل المثال، يمكنك أن ترى أن القسم 5 مكرس لهذه المسألة. فقط جوجل لذلك، وأنا متأكد من أنك ستجد الكثير من الأمثلة.

لقد كتبت محلل XSLT مع Flex و Bison. في الآونة الأخيرة، أقوم بمشروع باستخدام Antlr، على الرغم من:

هو JFIG بناء جملة لغة فعالة وواضحة (وأفضل من XML DSL) XML DSL)؟

لقد أحببت العمل في Antlr أكثر من ذلك بكثير من المرن والبيسون. يضعك Antll بمستوى أعلى من التجريد في بعض النواحي. يمكن أن تذهب التعريفات المعجمية والنحوي المحللون جميعا في ملف واحد. (سيؤدي Antllr إلى إنشاء ملف الرمز المميز.)

أحد العناصر الرئيسية هو القدرة على تحديد قواعد النهر الشجرة. في الأساس، تقوم بتحليل قواعد اللغة على لغة الإدخال ولدي إجراءات تعيد كتابة إخراج شجرة AST الأمثل للغاية (والتي لا تزال على أنها هياكل بيانات مرتبطة في الذاكرة). يمكنك بعد ذلك اجتياز هذه الشجرة إلى محلل محلل آخر محدد في ملف محلل شجرة منفصل. محلل شجرة هو المكان الذي تقوم فيه بالعمل الحقيقي لعناصر الإجراء التي تريدها.

هذا نهج جميل حيث يمكنك الاحتفاظ بنموذج AST وإعادة معالجةها بشكل متكرر حسب الحاجة - تقشير العقد الفرعية المحددة المعينة لمعالجة ضد الإجراءات الأخيرة، وما إلى ذلك. فكر في شيء مثل لغة تفسير اللغة. بدلا من الدخول إلى حلقة ومعالجة اللغة من الألف إلى الياء مرارا وتكرارا، يمكن أن تعالج فقط من خلال تمثيل AST.

في حالتي، ابتكرت مصنع الفاصوليا للقيام بحقنة الاعتماد IOC. مصنع الفول الخاص بي يبقي AST من واصف الفاصوليا حول وقت التشغيل. عندما يحتاج إلى إصدار (أو استرجاع) مثيل فول جديد، أترك فقط واصف الفاصوليا AST Subtree إلى محلل أشجار بلدي - النتيجة هي مثيل الفول المطلوب (هناك الكثير من العوامل التي تدخل لتحديد كيفية إنشاء مثيل الفاصوليا المطلوبة، بما في ذلك صنع أي فاصوليا أخرى المشار إليها و / أو تطبيق السلوكيات الخاصة الأخرى عبر سمات التعريف).

أخيرا، يستهدف مصنع الفول الحالي جافا، لكنني أرغب في استهداف ActionScript3 و C # .NET. Antlr لديه دعم للقيام بذلك.

كما ذكر، كتبت Berrence Parr كتابا حول كيفية استخدام Antlr. يهدف إلى أن يعمل المبرمجون الذين يحتاجون إلى القيام بشيء عملي مع Antlr (بدلا من العلاج الأكاديمي للموضوع).

إذا كنت تريد الرغبة C # حسب هذا سؤال جرب جاردنز بوينت GPPG و Glex.

عند استخدام LEX و YACC، لا تكتب في الواقع الكثير من أي شيء في C. ليكس هي لغتها الخاصة، كما هو YACC. أنت تكتب المحلل المعجمي في Lex و المحلل إكاك. وبعد ومع ذلك، بالنسبة إلى مدخلات Pascal و Lex و YACC المتاحة بالفعل.

الحظائر الناتجة وجهاز Lexer لديها واجهات ج، وهذا صحيح. ومع ذلك، فإن معظم اللغات، بما في ذلك C ++، لديها طرق بسيطة للاتصال (أو التفاف) C واجهات C.

أنا لست خبيرا فيه، لكنني متأكد من أن كل ما سبق يذهب إلى Antlr كذلك.

إذا كنت تطلب القيام بذلك في "C + C ++" (مهما كان ذلك يعني)، فابحث في استخدام دفعة روح. وبعد لا أرى حقا النقطة في الطهارة النظرية إذا كان الأمر يسبب الكثير من العمل أكثر.

كتابة لجهاز Lexters والتحلل الخاصة بك باليد هو في الواقع متعة كيندا. Lexer هي واحدة من القليل من المواقف التي يمكنك تبريرها باستخدام كليهما اذهب إلىS و Preprocessor. ومع ذلك، لن أقترح ذلك بلغة كاملة مثل Pascal إذا كنت تستطيع تجنب ذلك. سيكون ذلك الكثير من العمل. أنا أتحدث عن سنوات الرجل.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top