2016-05-15 3 views
1

私はAMPL言語のいくつかのサブセットのためのレクサーを構築しようとしています。 今、レクサーが扱っているシンボル型の名前が今必要です。 すべてのシンボル名は、varまたはparamまたはsetです。幸いなことに、それらのすべてを使用する前に宣言する必要があります。だから、私は単に私はyytextが第二が含まれていないため、仕事に行くのではないことを知っているこのどのように私はバッファからそれを削除せずにフレックスで次のトークンを覗くことができます

SYMBOLIC_NAME [a-zA-Z_][a-zA-Z0-9_]* 
%{ 
#include <vector> 
#include <algorithm> 
std::vector<std::string> paramNames; 
std::vector<std::string> setNames; 
std::vector<std::string> varNames; 

%} 
%% 
param/(.|\n)+{SYMBOLIC_NAME}    { paramNames.push_back(&yytext[5]); 
              return PARAM; } 
var/(.|\n)+{SYMBOLIC_NAME}    { varNames.push_back(&yytext[3]); 
              return VAR; } 
set/(.|\n)+{SYMBOLIC_NAME}    { setNames.push_back(&yytext[3]); 
              return SET; } 
{SYMBOLIC_NAME} { if (std::find(setNames.begin(), setNames.end(), yytext) != setNames.end()) { 
        yylval.string = (char*) strdup(yytext); 
        return SET_NAME; 
        } 

        if (std::find(paramNames.begin(), paramNames.end(), yytext) != paramNames.end()){ 
        yylval.string = (char*) strdup(yytext); 
        return PARAM_NAME; 
        } 
        if (std::find(varNames.begin(), varNames.end(), yytext) != varNames.end()){ 
        yylval.string = (char*) strdup(yytext); 
        return VAR_NAME; 
        } 
       } 
%% 

よう

SYMBOLIC_NAME [a-zA-Z_][a-zA-Z0-9_]* 
%% 
param    { return PARAM; } 
var    { return VAR; } 
set    { return SET; } 
{SYMBOLIC_NAME} { yylval.string = (char*) strdup(yytext); 
        return SYMBOLIC_NAME; 
        } 
%% 

からいくつかのことにレクサーのコードを変更するフレックスで前方参照演算子を使用することができます思いました最初の3つの正規表現の一部。 (。| \ n)+ {SYMBOLIC_NAME}の下にあるものをどうやって調べることができますか?

PS

私はコードが最適ではない知っているが、それはここでの問題ではありません:D

答えて

2

私はあなたが見ているどのような種類のシンボルテーブルをチェックしようとしていると思います。

この場合、シンボルテーブルを使用して通信する必要があります。つまり:

  1. 単純な「シンボル」ルールを作成します。あなたのオリジナルのルールが細かいです:今すぐ戻って、定義されたシンボルを確認するためにあなたのSYMBOLIC_NAMEルールを拡張

    var_decl : VAR SYMBOLIC_NAME 
        { add name to symbol table } 
    
  2. {SYMBOLIC_NAME} { 
          yylval.string = (char*) strdup(yytext); 
    
          if (std::find(setNames.begin(), setNames.end(), yytext) != setNames.end()) { 
           return SET_NAME; 
          } 
          else if (... varNames ...) { 
           return VAR_NAME; 
          else if (... paramNames ...) { 
           return PARAM_NAME; 
          } 
          else { 
           return SYMBOLIC_NAME; 
          } 
         } 
    
  3. {SYMBOLIC_NAME} { yylval.string = (char*) strdup(yytext); 
           return SYMBOLIC_NAME; 
           } 
    
  4. は、パーサレベルでの宣言構文をハンドル今

あなたは四つの可能なトークンを返す1つのFlexの目標を持って、D定義されたネスに支えられています。しかし、Flexはシンボル定義がアクティブであることを覚えておく心配がありません。パーサがそれを処理できるようにすることができます。パーサ側で

、あなたは異なるルールを記述します。

var_decl: VAR SYMBOLIC_NAME 
set_decl: SET SYMBOLIC_NAME 

expr: atom '+' atom 
atom: VAR_NAME | SET_NAME | PARAM_NAME 
+0

驚くばかりです。ソリューションをありがとう – Lisu

1

あなたが効果的に開始条件を使用して「絞り」を行うこともできますが、あなたが実際に何をしようとしている場合doはシンボルテーブルを維持しており、レクサーがシンボルごとに正しいセマンティックカテゴリを自動的に返すようにした方が良い解決法があります(下記参照)。

まず、代わりに3つのstd::vectorリストを使用して、あなたがシンボルを見つけるまで、直線的にそれぞれ1を検索するのは、あなたが本当に意味的なタイプでそれぞれの名前を関連付けるために、単一のstd::unordered_mapを使用したほうが良いでしょう。 (コードの非効率性を考えないと言ったことは知っていますが、この変更によりかなり簡単になります)レクサーにシンボルテーブルの管理を担当させたい場合は、それを簡単に行うことができますパーサは各シンボルに関連付けられた意味情報を格納する必要があるため、少し強制されますが、実行されます。まったく同じ、それほど苦痛ではありません。以下では、定義キーワードの後に​​定義された名前を収集するために単一の開始条件を使用しています(これは基本的にあなたの先読みですが、この方法で可変長の空白で始まる文字列の代わりに定義されている実際の名前を分離します)。

ここでは、ハッシュテーブルエントリの内部のC文字列にyylval.stringを設定することによって、ハッシュテーブルにシンボルの名前を表すstd::stringが含まれているという事実を利用します。 yylval.stringの内容を変更しない限り、記号はシンボルテーブルから削除されず、ハッシュテーブルはその要素を決して移動しないので、完全に安全です。実際には、うまくyylval組合員にする方がよいかもしれません:

%union { 
    std::string* string; 
    // ... 
} 

をそれはマイナーなディテールです。

%{ 
    #include <unordered_map> 
    namespace { 
    enum class Kind { UNDEFINED, PARAM, SET, VAR }; 
    std::unordered_map<std::string, Kind> symbols; 
    } 
%} 
%x SC_DEFINE 

id [[:alpha:]_][[:alnum:]_]* 

%% 
    /* Up to the first unindented line is inserted at the beginning of yylex */ 
    Kind to_define; 

<*>[[:space:]] /* Ignore in all start conditions */ 
param   { kind_to_define = Kind::PARAM; BEGIN(SC_DEFINE); } 
set   { kind_to_define = Kind::SET; BEGIN(SC_DEFINE); } 
var   { kind_to_define = Kind::VAR; BEGIN(SC_DEFINE); } 
{id}   { auto it = symbols.emplace(yytext, Kind::UNKNOWN).first; 
       yylval.string = it->first.c_str(); 
       switch (it->second) { 
        case Kind::PARAM: return PARAM_NAME; 
        case Kind::SET: return SET_NAME; 
        case Kind::VAR: return VAR_NAME; 
        default:   return UNDEFINED_NAME; 
       } 
       } 
<SC_DEFINE>{id} { auto itbool = symbols.emplace(yytext, to_define); 
        if (!itbool.second) { 
        if (itbool.first->second != Kind::UNKNOWN) { 
         /* Redefinition: handle the error somehow */ 
        } else { 
         /* Used previously, error presumably already issued */ 
         itbool.first->second = to_define; 
        } 
        } 
        BEGIN(INITIAL); 
        yylval.string = itbool.first->first.c_str(); 
        switch (to_define) { 
        case Kind::PARAM: return DEFINE_PARAM; 
        case Kind::SET: return DEFINE_SET; 
        case Kind::VAR: return DEFINE_VAR; 
        default: /* Logic error */ 
        } 
       } 

注:トークン上記リターンDEFINE_PARAM(たとえば)既に(yylval.stringに)シンボルの名前を示しので、あなたの文法規則は、のためでなければならないであろう、ここでスキャナです例:param_definition: PARAM SYMBOL ...の代わりにparam_definition: DEFINE_PARAM ...私は上記しなかった

ことの一つは、SC_DEFINE開始条件で他のエントリに記入している。他のトークンがあなたが(のようなvarsetを必要とするために起こる任意のキーワードトークンを含めて、(おそらく)構文エラーです例えば、param)。

実際にはコンパイルしようとはしませんでしたが、これはうまくいくと思います。しかし、それは少し不器用な以上です。

IMHOでは、パーサとレクサーの間でシンボルテーブルを共有する方がずっと良い方法です。 (バイソンのマニュアルでは、yylexに余分な引数を与える方法が説明されています。フレックスマニュアルにはそれを受け取る方法が説明されています)。基本的なシンボルテーブルは、パーサに属することを除いて上記と同じです。パーサを呼び出します。しかし、それはたぶん記号の種類よりも意味情報が多いでしょう。マップされた値は、識別されたユニオン、boost::variant、または他のどの便利なものであっても構いません。

この場合、スキャナは上記の概要と同じですが、開始条件はありません。

param_definition: PARAM UNDEFINED_NAME ... 

とのためのセマンティックアクションで:それは(それが定義キーワードに続くシンボルをスキャンする必要がある場合など)未定義シンボルを見つけると、あなたのパーサのルールは、次のようになりますので、それは、UNDEFINED_NAMEトークンを返します。パーサーはシンボルの種類を他の有用な情報と共に記入します。検索は、する必要がないように、yylval.symbolが(安定して無効にすることができないイテレータ、のではなく、&*iterator)シンボルテーブル内の値へのポインタをするため、この場合には、それはおそらく便利です繰り返される。このシナリオでは

、パーサーはこれらの場合を扱うはルールを持たないので、未定義のシンボルを使用して、すでに定義されたシンボルを定義しようとすると、両方の天然に、構文エラーになります。意味のあるエラーメッセージを提供するには、エラーケースなどのルールを追加するとよいでしょう。

0

あなたは文法に気にしてはいけません。あなたの文法はちょうどIDENTIFIERを処理して、セマンティックアクションのシンボルテーブル自体を検索する必要があります。

レクサーの中でこのようなことが起こっているのは間違った方法です。

関連する問題