سؤال

أحاول استخدام flex وbison لإنشاء مرشح، لأنني أريد الحصول على عناصر نحوية معينة من لغة معقدة.خطتي هي استخدام flex + bison للتعرف على القواعد النحوية والتخلص من موقع العناصر محل الاهتمام.(ثم ​​استخدم برنامجًا نصيًا للحصول على النص وفقًا للمواقع التي تم تفريغها.)

لقد وجدت أن flex يمكن أن يدعم ميزة البيسون التي تسمى مواقع البيسون، ولكن كيف تعمل بالضبط.لقد جربت المثال في المستند المرن، ويبدو أن yylloc لم يتم تعيينه تلقائيًا بواسطة flex، وهو ما أحصل عليه دائمًا (1,0)-(1,0).هل يستطيع flex حساب موقع كل رمز مميز تلقائيًا؟إذا لم يكن الأمر كذلك، ما هي وظيفة الواجهة التي تم تحديدها لي لتنفيذها؟هل هناك أي مثال؟

أي حل أفضل فيما يتعلق بالأدوات؟

مع أطيب التحيات ، كيفن

يحرر:

الآن تتحول واجهة yylex إلى:

int yylex(YYSTYPE * yylval_param,YYLTYPE * yylloc_param );

لا يحدد دليل البيسون كيفية تنفيذ lexer لتعيين yylloc_param بشكل صحيح.بالنسبة لي، من الصعب تتبع رقم العمود لكل رمز يدويًا.

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

المحلول

نلقي نظرة على القسم 3.6 من دليل البيسون - يبدو أن هذا يغطي المواقع بشيء من التفصيل.بالإضافة إلى ما وجدته في دليل Flex، قد يكون ذلك كافيًا.

نصائح أخرى

ربما تغير إعلان yylex لأنك استخدمت مُعيد الدخول أو محللًا خالصًا.يبدو أن العديد من المستندات الموجودة على الويب تشير إلى أنه مطلوب إذا كنت تريد أن تعمل مواقع البيسون ولكنه ليس مطلوبًا.

كنت بحاجة إلى أرقام الأسطر أيضًا ووجدت وثائق Bison مربكة في هذا الصدد.الحل البسيط (باستخدام var yylloc العالمي):في ملف Bison الخاص بك، قم فقط بإضافة توجيه %locations:

%{
...
%}
%locations
...
%%
...

في ليكسر الخاص بك:

%{
...
#include "yourprser.tab.h"  /* This is where it gets the definition for yylloc from */
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%}
%option yylineno
...
%%
...

يتم "استدعاء" الماكرو YY_USER_ACTION قبل كل إجراء من إجراءات الرمز المميز وتحديثات yylloc.يمكنك الآن استخدام قواعد @N/@$ مثل هذا:

statement : error ';'   { fprintf(stderr, "Line %d: Bad statement.\n", @1.first_line); }

, أو استخدم yylloc global var:

void yyerror(char *s)
{
  fprintf(stderr, "ERROR line %d: %s\n", yylloc.first_line, s);
}

تعجبني إجابة شلومي.

بالإضافة إلى ذلك كنت أبحث عن تحديث موقع العمود أيضًا.وجد http://oreilly.com/linux/excerpts/9780596155971/error-reporting-recovery.html وهو الأمر الذي أصبح أكثر منطقية بعد قراءة إجابة شلومي.

لسوء الحظ هناك خطأ مطبعي في تلك الصفحة لـ yylloc.لقد قمت بتبسيط الأمر أدناه قليلاً.

أضف في المحلل اللغوي الخاص بك:

%locations

في ليكسر الخاص بك:

%{

#include "parser.tab.h"

int yycolumn = 1;

#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno; \
    yylloc.first_column = yycolumn; yylloc.last_column = yycolumn + yyleng - 1; \
    yycolumn += yyleng; \
    yylval.str = strdup(yytext);

%}

%option yylineno

قد يكون هناك شيء ما يحدث في موقع العمود والذي لا يتتبع الأعمدة بشكل صارم ولكنه يستمر في الزيادة.هذا مجرد جهلي وأعتذر إذا كان ذلك يربك أحدا.أستخدم العمود حاليًا للاحتفاظ بعدد أحرف الملف وهو في حالتي أكثر فائدة من موقع العمود.

امل ان يساعد.

لا bison ولا flex التحديثات yylloc تلقائيًا، ولكن في الواقع ليس من الصعب القيام بذلك بنفسك، إذا كنت تعرف الحيلة.

الحيلة في التنفيذ yylloc الدعم هو ذلك، على الرغم من yyparse() يعلن yylloc, ، فإنه لا يغيره أبدا.وهذا يعني أنه إذا قمت بالتعديل yylloc في مكالمة واحدة للقارئ، ستجد نفس القيم فيه في المكالمة التالية.هكذا، yylloc سيحتوي على موضع الرمز الأخير.نظرًا لأن نهاية الرمز المميز الأخير هي نفس بداية الرمز المميز الحالي، فيمكنك استخدام الرمز القديم yylloc القيمة لمساعدتك في تحديد القيمة الجديدة.

بعبارة أخرى، yylex() لا يجب احسب yylloc;أنه ينبغي تحديث yylloc.

للتحديث yylloc, ، يجب علينا أولا نسخ last_ القيم ل first_, ، ثم قم بتحديث last_ القيم لتعكس طول الرمز المميز المطابق للتو.(ليس هذا strlen() من الرمز؛إنه طول الخطوط والأعمدة.) يمكننا القيام بذلك في YY_USER_ACTION الماكرو، والذي يتم استدعاؤه قبل تنفيذ أي إجراء معجمي مباشرةً؛يضمن أنه إذا تطابقت قاعدة ما ولكنها لم تُرجع قيمة (على سبيل المثال، قاعدة تتخطى المسافات البيضاء أو التعليقات)، فسيتم تخطي موقع هذا الرمز غير المميز، بدلاً من تضمينه في بداية الرمز المميز الفعلي، أو فقدت بطريقة تجعل تتبع الموقع غير دقيق.

فيما يلي إصدار مخصص للمحلل اللغوي المعاد إدخاله؛يمكنك تعديله لمحلل غير مُعاد الدخول عن طريق تبديل ملف -> مشغلين ل .:

#define YY_USER_ACTION \
    yylloc->first_line = yylloc->last_line; \
    yylloc->first_column = yylloc->last_column; \
    for(int i = 0; yytext[i] != '\0'; i++) { \
        if(yytext[i] == '\n') { \
            yylloc->last_line++; \
            yylloc->last_column = 0; \
        } \
        else { \
            yylloc->last_column++; \
        } \
    }

إذا كنت تفضل ذلك، يمكنك بدلاً من ذلك وضع هذا الرمز في دالة وجعل الماكرو يستدعي الوظيفة، ولكن الطريقتين متكافئتان.

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

إحدى الطرق للقيام بذلك هي إضافة yycolumn = 1 القواعد في كل مكان يظهر سطر جديد (كما هو مقترح في إجابة David Elson) ولكن إذا كنت لا تريد تتبع جميع الأماكن التي يمكن أن يظهر فيها سطر جديد (مسافة بيضاء، تعليقات، إلخ...) فإن البديل هو فحص yytext المخزن المؤقت في بداية كل إجراء:

static void update_loc(){
  static int curr_line = 1;
  static int curr_col  = 1;

  yylloc.first_line   = curr_line;
  yylloc.first_column = curr_col;

  {char * s; for(s = yytext; *s != '\0'; s++){
    if(*s == '\n'){
      curr_line++;
      curr_col = 1;
    }else{
      curr_col++;
    }
  }}

  yylloc.last_line   = curr_line;
  yylloc.last_column = curr_col-1;
}

#define YY_USER_ACTION update_loc();

أخيرًا، هناك شيء واحد يجب ملاحظته وهو أنه بمجرد البدء في تتبع أرقام الأعمدة يدويًا، يمكنك أيضًا تتبع أرقام الأسطر في نفس المكان وعدم الاهتمام باستخدام Flex's yylineno خيار.

لذا، لقد "نجحت" في هذا الأمر، ولكن مع بضع خطوات إضافية (ربما أكون قد تجاهلتها هنا...الاعتذار في هذه الحالة):

  1. في المحلل اللغوي.y, ، كان علي أن أقول:

    #define YYLEX_PARAM &yylval, &yylloc
    

    حتى مع %locations و bison --locations, ، للحصول عليه لتمرير البيانات.

  2. في lexer.l كان علي أن أستخدم -> بدلاً من . ل yylloc

  3. ايضا في lexer.l, ، قمت بإعادة تعيين العمود في الإجراء:

    [\n] { yycolumn = 1; }
    

من الواضح أن الأمر أكثر تعقيدًا بعض الشيء، لأنه \r إلخ، لكن على الأقل تمكنت من العمل.

أعتقد أنني تمكنت من إنجاح الأمر (يعود الفضل إلى كاتب دليل البيسون ltcalc محلل معجمي).افتراضيًا، يقوم البيسون بإنشاء yylloc الذي يحتوي على

{ first_line, first_column , last_line , last_column }

نحتاج فقط إلى تحديث هذه القيم في محللنا المعجمي.السابق :

[ \t]     { ++yylloc.last_column; }
[\n]      { yyloc.last_column = 0; return EOL; }
[a-zA-Z]+ { 
            yylloc.last_column += strlen(yytext);
            return IDENTIFIER;
          }

الآن في البيسون، لاسترداد تلك الحقول:

statement : IDENTIFIER '=' expression 
            { printf("%d - %d\n", @1.last_line, @1.last_column); }

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

إضافة إلى إجابة شلومي:

إذا كنت تستخدم %define api.pure في bison لإنشاء محلل إعادة الدخول، فستحتاج أيضًا إلى تحديد %option bison-locations في flex.وذلك لأنه في محلل إعادة الدخول، لا يعد yylloc متغيرًا عامًا، ويجب تمريره إلى المعجم.

لذلك، في المحلل:

%define api.pure
%locations

في ليكسر:

#include "yourprser.tab.h"
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%option bison-locations
%option yylineno
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top