23

複雑な言語から特定の文法要素を取得したいので、flex と bison を使用してフィルターを作成しようとしています。私の計画は、flex + bison を使用して文法を認識し、関心のある要素の場所をダンプすることです。(次に、スクリプトを使用して、ダンプされた場所に従ってテキストを取得します。)

flex は bison-locations と呼ばれる bison 機能をサポートできることがわかりましたが、それが正確にどのように機能するか. 私はフレックスドキュメントで例を試しました.yyllocはフレックスによって自動的に設定されていないようです.私はいつも(1,0)-(1,0). flex は各トークンの位置を自動的に計算できますか? そうでない場合、実装するためにどのインターフェイス関数が定義されていますか? 例はありますか?

ツールに関するより良い解決策はありますか?

よろしく、 ケビン

編集:

yylex のインターフェースは次のようになります。

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

bison のマニュアルでは、yylloc_param を正しく設定するためにレクサーがどのように実装する必要があるかを指定していません。私にとって、各トークンの列番号を手動で追跡するのは困難です。

4

8 に答える 8

22

再入可能または純粋なパーサーを使用したため、yylex宣言が変更された可能性があります。ウェブ上の多くのドキュメントが、バイソンの場所を機能させるために必要であると示唆しているようですが、必須ではありません。

私も行番号が必要でしたが、Bisonのドキュメントはその点で混乱していました。簡単な解決策(グローバル変数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グローバル変数を使用します。

void yyerror(char *s)
{
  fprintf(stderr, "ERROR line %d: %s\n", yylloc.first_line, s);
}
于 2011-04-27T22:53:05.500 に答える
15

シュロミの答えが好きです。

さらに、列の場所の更新も探していました。http://oreilly.com/linux/excerpts/9780596155971/error-reporting-recovery.htmlが見つかりました。これは、Shlomi の回答を読んだ後でより意味のあるものになりました。

残念ながら、そのページには 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

列を厳密に追跡するのではなく、単に増加し続ける列の場所で何かが起こっている可能性があります。それは私の無知であり、誰かを混乱させた場合は謝罪します. 現在、列を使用してファイルの文字数を保持しています。これは、私の場合、列の場所よりも有益です。

それが役立つことを願っています。

于 2011-11-06T02:30:50.947 に答える
7

行番号を保持することのみを気にする場合、ショミの答えは最も簡単な解決策です。ただし、列番号も必要な場合は、それらを追跡する必要があります。

これを行う1つの方法は、改行が表示されるすべての場所にルールを追加することです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 のyylinenoオプションを使用する必要がないことに注意してください。

于 2013-10-03T00:58:19.287 に答える
4

それで、私はこれを「機能」させましたが、いくつかの追加の手順があります(ここで見落としている可能性があります...その場合はお詫びします):

  1. parser.yでは、次のように言わなければなりませんでした。

    #define YYLEX_PARAM &yylval, &yylloc
    

    %locationsとを使用してもbison --locations、データを渡すことができます。

  2. lexer.lでは、 for->の代わりに使用する必要がありました.yylloc

  3. また、lexer.lで、アクションの列をリセットしました:

    [\n] { yycolumn = 1; }
    

明らかにもう少し複雑です\rが、少なくとも私はそれを機能させました。

于 2012-06-01T22:03:51.073 に答える
2

シュロミの答えへの追加:

bison で %define api.pure を使用してリエントラント パーサーを作成している場合は、flex で %option bison-locations も指定する必要があります。これは、再入可能パーサーでは 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
于 2015-11-09T16:27:17.710 に答える
1

私はなんとかそれを機能させることができたと思います(バイソンマニュアルltcalc字句解析器の作者にクレジットが行きます)。デフォルトでは、bison は以下を含む 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); }

デフォルトでは、これらのフィールドは 1 に初期化されます。列フィールドをゼロに初期化する必要があります。そうしないと、間違った列が報告されます。

于 2015-08-13T14:57:29.350 に答える