1

必ずしもすべてが同じタイプであるとは限らない要素の集まりと考えてください。次のコードがあります。

// The struct I'll use inside Bison to dynamically create collections:
typedef struct ListElementType {
    union value {
        int intVal;
        float floatVal;
        char* charptrVal;
    } value;

    struct ListElementType* next;
} ListElementType;

次に、バイソンには次のものがあります。

%union
{
    int int_type;
    char char_type;
    float float_type;
    char* charptr_type;
    ListElementType* listElementType;
}
//----------------------------------------------------
%token <charptr_type> STRING
%token <int_type> INTEGER
%token <float_type> REAL
%type<listElementType> ElementList
//----------------------------------------------------
//----------------------------------------------------
ElementList
: ElementList ',' LiteralType
{ 
    $$ = malloc(sizeof(listElementType));
    $$->next = $1;
    $$->value = $3;
}

| LiteralType
{ 
    $$ = malloc(sizeof(listElementType));
    $$->next = 0;
    $$->value = $1;
}
;
//----------------------------------------------------
LiteralType
: STRING
| INTEGER
| REAL
;

ここにはいくつかの問題があります。しかし、最初に、この Bison のようなパーサーを生成しようとすると、再帰生成の $3 と base /terminal ケースの $1 に宣言された型がないことがわかります。私が見ているように、実際には型が宣言されています。それらは LiteralType であり、文字列、int、または float のいずれかであり、最後のターミナル プロダクションを空白のままにして自動的に設定する必要があります (最初に行ったのは、グローバル ユニオンから適切なものを選択して型を明示したことを前提としています)。 .

第二に、宣言された型がないと Bison が文句を言うとは思いませんが、むしろ衝突やあいまいさがあります。メンバーはそれぞれの作品に配属されました)。この状況では、ListElementType 構造体の値メンバーを共用体にしました。構造体の最初のメンバーが構造体アドレス自体の「ラベル」の場所にあるという事実に加えて、ユニオンのメンバーもすべてユニオンのメモリアドレスから開始して、タイプ。(void )$$ = $2の行に沿った何か、たまたま $2 が何であれ。

SO、コードを次のように変更しました。

//----------------------------------------------------
ElementList
: ElementList ',' LiteralType
{ 
    $$ = malloc(sizeof(listElementType));
    $$->next = $1;
    *$$ = (void*)$3;
}

| LiteralType
{ 
    $$ = malloc(sizeof(listElementType));
    $$->next = 0;
    $$->value = $1;
}
;
//----------------------------------------------------
LiteralType
: STRING
{
    $<charptr_type>$ = $1;
}

| INTEGER
{
    $<int_type>$ = $1;
}

| REAL
{
    $<float_type>$ = $1;
}

;

これで、INT、REAL、STRING のケースの共用体を明示的に設定しました。必要ないと思ったのですが、間違っていたら誰かが訂正してください。そして、型のない共用体の代入も試しましたが、それでも同じエラー: $3 と $1 には宣言された型がありません。

だから私の考え、質問:

StringList、IntList、および RealList プロダクションを個別に作成する必要があります。唯一の変更点は、右側の非終端要素がリスト内の特定のタイプの要素をそのまま示していることです。

//----------------------------------------------------
ElementList
: IntElementList
| RealElementList
;

IntElementList
: IntElementList ',' INTEGER
{ 
    $$ = malloc(sizeof(listElementType));
    $$->next = $1;
    $$->intVal = $3;
}

| INTEGER
{ 
    $$ = malloc(sizeof(listElementType));
    $$->next = 0;
    $$->intVal = $1;
}

RealElementList
: RealElementList ',' REAL
{ 
    $$ = malloc(sizeof(listElementType));
    $$->next = $1;
    $$->floatVal = $3;
}

| REAL
{ 
    $$ = malloc(sizeof(listElementType));
    $$->next = 0;
    $$->floatVal = $1;
}

;

または、LiteralType が 3 つの値のいずれかを持つことができると述べてから、型のない共用体の割り当てを試してプルする方法はありますか?

それとも、アプローチ全体が間違っていて、より良い方法がありますか?

4

3 に答える 3

1

私は最終的にこのアプローチに行きました。

  1. Element プロダクション (例: LiteralType 非端末) を共用体に還元する代わりに、共用体と型メンバーを持つ構造体に還元することに注意してください。type メンバーは、コレクションに格納されている各要素の型を伝える方法です。
  2. また、ListType 構造体には要素への void* ポインターがあることに注意してください。この不自然な例では、ElementType 構造体型のメンバーで十分です。ただし、要素を汎用ポインターにして、同じ構造体を使用して、要素リストで構成される宣言リストを格納します。

%code には { typedef struct Element {

%code requires {
    typedef struct Element {
        union {
            int intVal;
            float floatVal;
            char* charptrVal;            
        };

    char type;

    } ElementType;

    typedef struct ListType {
        void* element;
        struct ListType* next;

    } ListType;
}

%union
{
    int int_type;
    char char_type;
    float float_type;
    char* charptr_type;
    ListType* ListType;
    ElementType* ElementType;
}



%token <charptr_type> KEYWORD
%token <charptr_type> ID
%token <charptr_type> STRING
%token <int_type> INTEGER
%token <float_type> REAL
%token END 0


%type<ElementType> Element
%type<ListType> ElementList

//----------------------------------------------------
ElementList
: Element ',' ElementList
{
    $$ = malloc(sizeof(ListType));
    $$->element = (void*)$1;
    $$->next = $3;
}

| Element
{
    $$ = malloc(sizeof(ListType));
    $$->element = (void*)$1;
    $$->next = NULL;
}
;
//----------------------------------------------------
Element
: STRING
{
    char* aString = malloc(sizeof(char)*strlen($1)+1);
    strcpy(aString, $1);
    free(yylval.charptr_type);

    $$ = malloc(sizeof(ElementType));
    $$->charptrVal = aString;
    $$->type = 's';
}
| INTEGER
{
    $$ = malloc(sizeof(ElementType));
    $$->intVal = $1;
    $$->type = 'i';
}

| REAL
{
    $$ = malloc(sizeof(ElementType));
    $$->floatVal = $1;
    $$->type = 'f';    
}
;
于 2012-10-16T19:40:20.563 に答える
1

一般的に、あなたがしたいことは、異種リストタイプにタイプタグを持たせることです:

typedef enum ListElementType { INTEGER, REAL, STRING } ListElementType
typedef struct ListElement {
    ListElementType  type;
    union {
        int intVal;
        float floatVal;
        char* charptrVal;
    } value;
    struct ListElement* next;
} ListElement;

次に、ListElement を作成するたびに、typeフィールドを適切に設定します。後でtypeフィールドをチェックして、それが何であるかを確認できます。

バイソンコードは次のようになります。

%union
{
    int int_type;
    char char_type;
    float float_type;
    char* charptr_type;
    ListElement* listElement;
    struct { ListElement *head, *tail } list;
}
//----------------------------------------------------
%token <charptr_type> STRING
%token <int_type> INTEGER
%token <float_type> REAL
%type<list> ElementList
%type<listElement> LiteralType
//----------------------------------------------------
%%
//----------------------------------------------------
ElementList
: ElementList ',' LiteralType
    { $$.head = $1.head;
      $$.tail = $1.tail->next = $3; }
| LiteralType
    { $$.head = $$.tail = $1; }
;
//----------------------------------------------------
LiteralType
: STRING  { ($$ = NewListElement(STRING))->value.charptrVal = $1; }
| INTEGER { ($$ = NewListElement(INTEGER))->value.intVal = $1; }
| REAL    { ($$ = NewListElement(REAL))->value.floatVal = $1; }
;
%%
ListElement *NewListElement(ListElementType type) {
    ListElement *rv = malloc(sizeof(ListElement));
    rv->type = type;
    rv->next = 0;
    return rv; }
于 2012-10-16T21:11:39.530 に答える
0

Bison が完全な C 型チェックを実装しようとしないという事実を見逃していると思います。STRING と LiteralType に異なる型名を付けたので、そのデフォルト アクション ($$ = $1) が (bison-) 型チェックの観点から奇妙なことを行うことを報告するのがそのタスクです。デフォルトの割り当てを使用したい場合は、それらに同じタイプ(あなたの場合の値)を与えてください。

また、ユニオン値を 2 回コーディングしていますが、これは必要ないようです。

%code requires
{
  typedef struct ListElementType {
    union value {
      int intVal;
      float floatVal;
      char* charptrVal;
    } value;

    struct ListElementType* next;
  } ListElementType;
}

%union
 {
   union value value;
   ListElementType* list;
 };

%token <value> STRING INTEGER REAL
%type <value> LiteralType 
%type <list> ElementList
%%
ElementList
: ElementList ',' LiteralType
{ 
  $$ = malloc(sizeof($$));
  $$->next = $1;
  $$->value = $3;
}
| LiteralType
{ 
  $$ = malloc(sizeof($$));
  $$->next = 0;
  $$->value = $1;
}
;
//----------------------------------------------------
LiteralType
: STRING
| INTEGER
| REAL
;
于 2012-10-16T07:45:06.443 に答える